@sentry/junior 0.90.0 → 0.92.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/{agent-hooks-MXAF7RQL.js → agent-hooks-BYSSWCNW.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 +406 -267
  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 +4 -4
  15. package/dist/chat/agent/request.d.ts +21 -13
  16. package/dist/chat/agent/resume.d.ts +5 -4
  17. package/dist/chat/agent/tools.d.ts +2 -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/current-instruction.d.ts +5 -3
  27. package/dist/chat/logging.d.ts +2 -2
  28. package/dist/chat/no-reply.d.ts +1 -1
  29. package/dist/chat/oauth-authorization-message.d.ts +6 -0
  30. package/dist/chat/oauth-flow.d.ts +1 -1
  31. package/dist/chat/pi/messages.d.ts +2 -2
  32. package/dist/chat/pi/transcript.d.ts +15 -0
  33. package/dist/chat/plugins/agent-hooks.d.ts +3 -3
  34. package/dist/chat/plugins/credential-hooks.d.ts +2 -8
  35. package/dist/chat/prompt.d.ts +3 -6
  36. package/dist/chat/runtime/reply-executor.d.ts +1 -1
  37. package/dist/chat/runtime/turn-input.d.ts +1 -1
  38. package/dist/chat/sandbox/egress/schemas.d.ts +2 -2
  39. package/dist/chat/sandbox/egress/session.d.ts +1 -1
  40. package/dist/chat/services/context-compaction.d.ts +1 -1
  41. package/dist/chat/services/conversation-memory.d.ts +1 -1
  42. package/dist/chat/services/mcp-auth-orchestration.d.ts +1 -1
  43. package/dist/chat/services/message-actor-identity.d.ts +4 -4
  44. package/dist/chat/services/pending-auth.d.ts +2 -2
  45. package/dist/chat/services/plugin-auth-orchestration.d.ts +1 -1
  46. package/dist/chat/services/provider-default-config.d.ts +1 -1
  47. package/dist/chat/services/subscribed-decision.d.ts +1 -1
  48. package/dist/chat/services/turn-result.d.ts +1 -1
  49. package/dist/chat/services/turn-session-record.d.ts +11 -8
  50. package/dist/chat/services/turn-thinking-level.d.ts +1 -1
  51. package/dist/chat/slack/assistant-thread/title.d.ts +1 -1
  52. package/dist/chat/slack/mrkdwn.d.ts +6 -0
  53. package/dist/chat/slack/tools/canvas/create.d.ts +41 -1
  54. package/dist/chat/slack/tools/canvas/edit.d.ts +60 -1
  55. package/dist/chat/slack/tools/canvas/read.d.ts +60 -1
  56. package/dist/chat/slack/tools/canvas/write.d.ts +41 -1
  57. package/dist/chat/slack/tools/channel-list-messages.d.ts +45 -1
  58. package/dist/chat/slack/tools/context.d.ts +3 -3
  59. package/dist/chat/slack/tools/list/add-items.d.ts +42 -1
  60. package/dist/chat/slack/tools/list/create.d.ts +40 -1
  61. package/dist/chat/slack/tools/list/get-items.d.ts +40 -1
  62. package/dist/chat/slack/tools/list/update-item.d.ts +46 -1
  63. package/dist/chat/slack/tools/message-add-reaction.d.ts +40 -1
  64. package/dist/chat/slack/tools/send-message.d.ts +45 -1
  65. package/dist/chat/slack/tools/thread-read.d.ts +44 -1
  66. package/dist/chat/slack/tools/user-lookup.d.ts +45 -1
  67. package/dist/chat/slack/user.d.ts +3 -3
  68. package/dist/chat/slack/vision-context.d.ts +2 -2
  69. package/dist/chat/sql/schema.d.ts +2 -19
  70. package/dist/chat/state/conversation-details.d.ts +4 -4
  71. package/dist/chat/state/conversation.d.ts +1 -1
  72. package/dist/chat/state/session-log.d.ts +140 -26
  73. package/dist/chat/state/turn-session.d.ts +18 -5
  74. package/dist/chat/task-execution/slack-work.d.ts +2 -2
  75. package/dist/chat/task-execution/state.d.ts +4 -4
  76. package/dist/chat/tool-support/zod-tool.d.ts +16 -4
  77. package/dist/chat/tools/advisor/tool.d.ts +41 -1
  78. package/dist/chat/tools/definition.d.ts +10 -0
  79. package/dist/chat/tools/execute-tool.d.ts +41 -1
  80. package/dist/chat/tools/resource-events.d.ts +124 -3
  81. package/dist/chat/tools/runtime/report-progress.d.ts +40 -1
  82. package/dist/chat/tools/sandbox/bash.d.ts +41 -1
  83. package/dist/chat/tools/sandbox/edit-file.d.ts +44 -1
  84. package/dist/chat/tools/sandbox/find-files.d.ts +42 -1
  85. package/dist/chat/tools/sandbox/grep.d.ts +46 -1
  86. package/dist/chat/tools/sandbox/list-dir.d.ts +41 -1
  87. package/dist/chat/tools/sandbox/read-file.d.ts +42 -1
  88. package/dist/chat/tools/sandbox/write-file.d.ts +41 -1
  89. package/dist/chat/tools/search-tools.d.ts +102 -1
  90. package/dist/chat/tools/skill/call-mcp-tool.d.ts +10 -1
  91. package/dist/chat/tools/skill/load-skill.d.ts +40 -1
  92. package/dist/chat/tools/skill/search-mcp-tools.d.ts +100 -1
  93. package/dist/chat/tools/system-time.d.ts +38 -1
  94. package/dist/chat/tools/types.d.ts +4 -4
  95. package/dist/chat/tools/web/fetch-tool.d.ts +41 -1
  96. package/dist/chat/tools/web/image-generate.d.ts +62 -1
  97. package/dist/chat/tools/web/search.d.ts +41 -1
  98. package/dist/{chunk-HXL2ZV74.js → chunk-3F54A3YM.js} +82 -11
  99. package/dist/{chunk-BKSZL4QO.js → chunk-7DVIELX3.js} +670 -401
  100. package/dist/{chunk-GCWD2VHP.js → chunk-7FS3K243.js} +284 -124
  101. package/dist/{chunk-AOISHLSF.js → chunk-ACJENBVF.js} +143 -20
  102. package/dist/{chunk-IRRMABQD.js → chunk-APIVR44P.js} +10 -10
  103. package/dist/{chunk-PHZHJTCK.js → chunk-BMQ5DXHV.js} +30 -52
  104. package/dist/{chunk-XP7F4LYB.js → chunk-BTH37NCU.js} +4 -4
  105. package/dist/{chunk-5GBUZI6M.js → chunk-H7KUZAMZ.js} +4 -4
  106. package/dist/{chunk-TYUXD3FN.js → chunk-MC4ZZE4Q.js} +1 -1
  107. package/dist/{chunk-QCKGI74V.js → chunk-N25HRI6B.js} +10 -10
  108. package/dist/{chunk-2S7M37HY.js → chunk-NIFONS4W.js} +86 -47
  109. package/dist/{chunk-ONFOWI2D.js → chunk-OJP53WIU.js} +16 -15
  110. package/dist/{chunk-NW7AXDA5.js → chunk-OZJXGX4U.js} +2 -2
  111. package/dist/{chunk-KC42JAAQ.js → chunk-PFXC67GJ.js} +3 -3
  112. package/dist/{chunk-ZUK7BL63.js → chunk-S6QKIGRM.js} +66 -54
  113. package/dist/{chunk-IX76WFJV.js → chunk-TF5VLGA7.js} +10 -10
  114. package/dist/{chunk-DACFZ5CI.js → chunk-VY7TVOIK.js} +16 -12
  115. package/dist/{chunk-AN2437E3.js → chunk-YTNPU7I2.js} +2 -2
  116. package/dist/{chunk-3MPOMK3K.js → chunk-YYBX2BD5.js} +61 -3
  117. package/dist/{chunk-RS6ANWVT.js → chunk-ZATRYQLR.js} +1 -1
  118. package/dist/cli/chat.js +20 -20
  119. package/dist/cli/check.js +3 -3
  120. package/dist/cli/plugins.js +9 -9
  121. package/dist/cli/snapshot-warmup.js +6 -6
  122. package/dist/cli/upgrade.js +9 -9
  123. package/dist/{db-CXI7PR5U.js → db-GCK43OO3.js} +4 -4
  124. package/dist/instrumentation.js +1 -1
  125. package/dist/reporting/conversations.d.ts +3 -3
  126. package/dist/reporting.d.ts +1 -1
  127. package/dist/reporting.js +48 -50
  128. package/dist/{runner-FKL3RAHA.js → runner-XWTFA26U.js} +20 -18
  129. package/dist/{validation-I7GD2YWS.js → validation-KYAWETI4.js} +3 -3
  130. package/package.json +6 -6
  131. 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-OJP53WIU.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,27 +23,25 @@ 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,
30
30
  buildSystemPrompt,
31
31
  buildTurnContextPrompt,
32
32
  containsNoReplyMarker,
33
- extractCurrentInstructionBody,
33
+ formatSlackLink,
34
34
  isNoReplyMarker,
35
- renderCurrentInstruction,
36
35
  stripNoReplyMarker
37
- } from "./chunk-PHZHJTCK.js";
36
+ } from "./chunk-BMQ5DXHV.js";
38
37
  import {
39
- escapeXml,
40
38
  loadConnectedMcpProviders,
41
39
  recordAuthorizationRequested,
42
40
  recordMcpProviderConnected,
43
41
  recordSubagentEnded,
44
42
  recordSubagentStarted,
45
43
  recordToolExecutionStarted
46
- } from "./chunk-GCWD2VHP.js";
44
+ } from "./chunk-7FS3K243.js";
47
45
  import {
48
46
  SlackActionError,
49
47
  createPluginHookRunner,
@@ -60,10 +58,10 @@ import {
60
58
  normalizeSlackConversationId,
61
59
  resolveChannelCapabilities,
62
60
  withSlackRetries
63
- } from "./chunk-DACFZ5CI.js";
61
+ } from "./chunk-VY7TVOIK.js";
64
62
  import {
65
63
  createPluginLogger
66
- } from "./chunk-NW7AXDA5.js";
64
+ } from "./chunk-OZJXGX4U.js";
67
65
  import {
68
66
  SANDBOX_DATA_ROOT,
69
67
  SANDBOX_SKILLS_ROOT,
@@ -74,11 +72,11 @@ import {
74
72
  import {
75
73
  acquireActiveLock,
76
74
  getStateAdapter
77
- } from "./chunk-RS6ANWVT.js";
75
+ } from "./chunk-ZATRYQLR.js";
78
76
  import {
79
77
  getConversationStore,
80
78
  getDb
81
- } from "./chunk-2S7M37HY.js";
79
+ } from "./chunk-NIFONS4W.js";
82
80
  import {
83
81
  GEN_AI_PROVIDER_NAME,
84
82
  GEN_AI_SERVER_ADDRESS,
@@ -87,6 +85,7 @@ import {
87
85
  botConfig,
88
86
  completeObject,
89
87
  completeText,
88
+ escapeXml,
90
89
  extractAssistantText,
91
90
  getGatewayApiKey,
92
91
  getPiGatewayApiKey,
@@ -99,19 +98,22 @@ import {
99
98
  nextProviderRetry,
100
99
  normalizeSlackEmojiName,
101
100
  normalizeToolNameFromResult,
101
+ renderCurrentInstruction,
102
102
  resolveGatewayModel,
103
103
  stripRuntimeTurnContext
104
- } from "./chunk-HXL2ZV74.js";
104
+ } from "./chunk-3F54A3YM.js";
105
105
  import {
106
106
  parseSlackThreadId,
107
107
  resolveConversationPrivacy,
108
108
  runWithConversationPrivacy,
109
+ toCanonicalInputMessage,
110
+ toCanonicalOutputMessage,
109
111
  toGenAiMessageMetadata,
110
112
  toGenAiMessagesTraceAttributes,
111
113
  toGenAiPayloadMetadata,
112
114
  toGenAiPayloadTraceAttributes,
113
115
  toGenAiTextMetadata
114
- } from "./chunk-3MPOMK3K.js";
116
+ } from "./chunk-YYBX2BD5.js";
115
117
  import {
116
118
  parseDestination
117
119
  } from "./chunk-GHGPTPBL.js";
@@ -120,20 +122,21 @@ import {
120
122
  findSkillByName,
121
123
  loadSkillsByName,
122
124
  parseSkillInvocation
123
- } from "./chunk-AN2437E3.js";
125
+ } from "./chunk-YTNPU7I2.js";
124
126
  import {
125
127
  CredentialUnavailableError,
126
128
  credentialContextSchema,
127
129
  pluginCatalogRuntime,
128
130
  resolveAuthTokenPlaceholder,
129
131
  resolvePluginCommandEnv
130
- } from "./chunk-XP7F4LYB.js";
132
+ } from "./chunk-BTH37NCU.js";
131
133
  import {
132
- createRequester,
134
+ createActor,
133
135
  extractGenAiUsageAttributes,
134
136
  extractGenAiUsageSummary,
135
137
  getLogContextAttributes,
136
138
  getTracePropagationHeaders,
139
+ isUserActor,
137
140
  logError,
138
141
  logException,
139
142
  logInfo,
@@ -145,7 +148,7 @@ import {
145
148
  setTags,
146
149
  summarizeMessageText,
147
150
  withSpan
148
- } from "./chunk-ZUK7BL63.js";
151
+ } from "./chunk-S6QKIGRM.js";
149
152
  import {
150
153
  isRecord
151
154
  } from "./chunk-OB42YVAE.js";
@@ -465,7 +468,7 @@ function buildChatStartAttributes(model, context, mode, conversationPrivacy) {
465
468
  ...toGenAiMessagesTraceAttributes("app.ai.input", context.messages)
466
469
  };
467
470
  const inputMessages = serializeGenAiAttribute(
468
- mode === "metadata" ? context.messages.map(toGenAiMessageMetadata) : context.messages
471
+ mode === "metadata" ? context.messages.map(toGenAiMessageMetadata) : context.messages.map(toCanonicalInputMessage)
469
472
  );
470
473
  if (inputMessages) {
471
474
  attributes["gen_ai.input.messages"] = inputMessages;
@@ -486,7 +489,7 @@ function buildChatEndAttributes(message, mode) {
486
489
  ...toGenAiMessagesTraceAttributes("app.ai.output", [message])
487
490
  };
488
491
  const outputMessages = serializeGenAiAttribute(
489
- mode === "metadata" ? [toGenAiMessageMetadata(message)] : [message]
492
+ mode === "metadata" ? [toGenAiMessageMetadata(message)] : [toCanonicalOutputMessage(message)]
490
493
  );
491
494
  if (outputMessages) {
492
495
  attributes["gen_ai.output.messages"] = outputMessages;
@@ -675,8 +678,7 @@ function buildTurnResult(input) {
675
678
  );
676
679
  const canvasCreated = successfulToolNames.has("slackCanvasCreate");
677
680
  const reactionPerformed = successfulToolNames.has("addReaction");
678
- const markerSideEffectSuccess = exactNoReplyMarker && toolErrorCount === 0 && reactionPerformed;
679
- const sideEffectOnlySuccess = markerSideEffectSuccess;
681
+ const silentCompletionSuccess = exactNoReplyMarker;
680
682
  const baseDeliveryPlan = {
681
683
  mode: "thread",
682
684
  postThreadText: true
@@ -689,7 +691,7 @@ function buildTurnResult(input) {
689
691
  const markerCategory = reactionPerformed ? "reaction" : "none";
690
692
  const markerContext = {
691
693
  slackThreadId: correlation?.threadId,
692
- slackUserId: correlation?.requesterId,
694
+ slackUserId: correlation?.actorId,
693
695
  slackChannelId: correlation?.channelId,
694
696
  runId: correlation?.runId,
695
697
  assistantUserName,
@@ -698,29 +700,22 @@ function buildTurnResult(input) {
698
700
  const markerAttributes = {
699
701
  "app.ai.no_reply_marker": true,
700
702
  "app.ai.no_reply_marker_category": markerCategory,
701
- "app.ai.no_reply_marker_accepted": sideEffectOnlySuccess && !isProviderError
703
+ "app.ai.no_reply_marker_accepted": !isProviderError
702
704
  };
703
- if (sideEffectOnlySuccess && !isProviderError) {
705
+ if (!isProviderError) {
704
706
  logInfo(
705
707
  "ai_no_reply_marker_accepted",
706
708
  markerContext,
707
709
  markerAttributes,
708
710
  "No-reply marker suppressed visible thread text"
709
711
  );
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
712
  }
718
713
  } else if (mixedNoReplyMarker) {
719
714
  logWarn(
720
715
  "ai_no_reply_marker_mixed_text",
721
716
  {
722
717
  slackThreadId: correlation?.threadId,
723
- slackUserId: correlation?.requesterId,
718
+ slackUserId: correlation?.actorId,
724
719
  slackChannelId: correlation?.channelId,
725
720
  runId: correlation?.runId,
726
721
  assistantUserName,
@@ -733,12 +728,12 @@ function buildTurnResult(input) {
733
728
  "No-reply marker appeared with visible assistant text"
734
729
  );
735
730
  }
736
- if (!primaryText && !sideEffectOnlySuccess && !isProviderError && !exactNoReplyMarker) {
731
+ if (!primaryText && !silentCompletionSuccess && !isProviderError) {
737
732
  logWarn(
738
733
  "ai_model_response_empty",
739
734
  {
740
735
  slackThreadId: correlation?.threadId,
741
- slackUserId: correlation?.requesterId,
736
+ slackUserId: correlation?.actorId,
742
737
  slackChannelId: correlation?.channelId,
743
738
  runId: correlation?.runId,
744
739
  assistantUserName,
@@ -756,7 +751,7 @@ function buildTurnResult(input) {
756
751
  let outcome;
757
752
  if (isProviderError) {
758
753
  outcome = "provider_error";
759
- } else if (primaryText || sideEffectOnlySuccess) {
754
+ } else if (primaryText || silentCompletionSuccess) {
760
755
  outcome = "success";
761
756
  } else {
762
757
  outcome = "execution_failure";
@@ -766,7 +761,7 @@ function buildTurnResult(input) {
766
761
  const escapedOrRawPayload = Boolean(primaryText) && (isExecutionEscapeResponse(primaryText) || isRawToolPayloadResponse(primaryText));
767
762
  const resolvedText = escapedOrRawPayload ? "" : responseText;
768
763
  const resolvedOutcome = escapedOrRawPayload ? "execution_failure" : outcome;
769
- const deliveryPlan = resolvedOutcome === "success" && !resolvedText && reactionPerformed ? {
764
+ const deliveryPlan = resolvedOutcome === "success" && !resolvedText ? {
770
765
  ...baseDeliveryPlan,
771
766
  postThreadText: false
772
767
  } : baseDeliveryPlan;
@@ -939,7 +934,7 @@ async function selectTurnThinkingLevel(args) {
939
934
  const logContext = {
940
935
  slackThreadId: args.context?.threadId,
941
936
  slackChannelId: args.context?.channelId,
942
- slackUserId: args.context?.requesterId,
937
+ slackUserId: args.context?.actorId,
943
938
  runId: args.context?.runId,
944
939
  modelId: args.fastModelId
945
940
  };
@@ -962,7 +957,7 @@ async function selectTurnThinkingLevel(args) {
962
957
  modelId: args.fastModelId,
963
958
  threadId: args.context?.threadId ?? "",
964
959
  channelId: args.context?.channelId ?? "",
965
- requesterId: args.context?.requesterId ?? "",
960
+ actorId: args.context?.actorId ?? "",
966
961
  runId: args.context?.runId ?? ""
967
962
  },
968
963
  prompt
@@ -1077,25 +1072,38 @@ function getSessionIdentifiers(routing) {
1077
1072
  sessionId: routing.correlation?.turnId
1078
1073
  };
1079
1074
  }
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
- });
1075
+ function actorFromRouting(routing) {
1076
+ if (routing.dispatch?.actor) {
1077
+ return routing.dispatch.actor;
1078
+ }
1079
+ const userActor = createActor(
1080
+ isUserActor(routing.actor) ? routing.actor : void 0,
1081
+ {
1082
+ platform: (isUserActor(routing.actor) ? routing.actor.platform : void 0) ?? (routing.destination.platform === "slack" ? "slack" : void 0),
1083
+ teamId: (routing.destination.platform === "slack" ? routing.destination.teamId : void 0) ?? routing.correlation?.teamId ?? (routing.actor?.platform === "slack" ? routing.actor.teamId : void 0),
1084
+ userId: routing.correlation?.actorId
1085
+ }
1086
+ );
1087
+ if (userActor) {
1088
+ return userActor;
1089
+ }
1090
+ if (routing.credentialContext && !("type" in routing.credentialContext.actor)) {
1091
+ return routing.credentialContext.actor;
1092
+ }
1093
+ return void 0;
1086
1094
  }
1087
- function assertRequesterDestinationMatch(routing) {
1088
- const { destination, requester } = routing;
1089
- if (!requester) {
1095
+ function assertActorDestinationMatch(routing) {
1096
+ const { destination, actor } = routing;
1097
+ if (!actor) {
1090
1098
  return;
1091
1099
  }
1092
- if (requester.platform !== destination.platform) {
1100
+ if (actor.platform !== destination.platform) {
1093
1101
  throw new TypeError(
1094
- `Requester platform "${requester.platform}" does not match destination platform "${destination.platform}"`
1102
+ `Actor platform "${actor.platform}" does not match destination platform "${destination.platform}"`
1095
1103
  );
1096
1104
  }
1097
- if (requester.platform === "slack" && destination.platform === "slack" && requester.teamId !== destination.teamId) {
1098
- throw new TypeError("Slack requester team does not match destination team");
1105
+ if (actor.platform === "slack" && destination.platform === "slack" && actor.teamId !== destination.teamId) {
1106
+ throw new TypeError("Slack actor team does not match destination team");
1099
1107
  }
1100
1108
  }
1101
1109
  function assertCorrelationDestinationMatch(routing) {
@@ -1347,15 +1355,12 @@ async function restoreSkillRuntime(args) {
1347
1355
  }
1348
1356
 
1349
1357
  // src/chat/agent/prompt.ts
1350
- import { z as z2 } from "zod";
1351
1358
  var MAX_INLINE_ATTACHMENT_BASE64_CHARS = 12e4;
1352
1359
  var MAX_ROUTER_ATTACHMENT_PREVIEW_CHARS = 2e3;
1353
- var legacyStoredTextPartSchema = z2.object({
1354
- text: z2.string(),
1355
- type: z2.literal("text")
1356
- }).strict();
1357
1360
  function isStructuredThreadContext(context) {
1358
- return /^<thread-(compactions|transcript)>/.test(context);
1361
+ return /^<(recent-thread-messages|thread-(compactions|transcript))>/.test(
1362
+ context
1363
+ );
1359
1364
  }
1360
1365
  function renderThreadContextForPrompt(context) {
1361
1366
  if (isStructuredThreadContext(context)) {
@@ -1363,9 +1368,9 @@ function renderThreadContextForPrompt(context) {
1363
1368
  }
1364
1369
  return ["<thread-background>", context, "</thread-background>"].join("\n");
1365
1370
  }
1366
- function buildUserTurnText(userInput, conversationContext) {
1371
+ function buildUserTurnText(userInput, conversationContext, actor) {
1367
1372
  const trimmedContext = conversationContext?.trim();
1368
- const currentInstruction = renderCurrentInstruction(userInput);
1373
+ const currentInstruction = renderCurrentInstruction(userInput, actor);
1369
1374
  if (!trimmedContext) {
1370
1375
  return currentInstruction;
1371
1376
  }
@@ -1498,10 +1503,11 @@ function buildUserTurnInput(args) {
1498
1503
  return { routerBlocks, userContentParts };
1499
1504
  }
1500
1505
  function buildPromptInput(input) {
1501
- const promptConversationContext = input.piMessages && input.piMessages.length > 0 ? void 0 : input.conversationContext;
1506
+ const promptConversationContext = input.piMessages && input.piMessages.length > 0 && !input.includeConversationContextWithPiMessages ? void 0 : input.conversationContext;
1502
1507
  const userTurnText = buildUserTurnText(
1503
1508
  input.messageText,
1504
- promptConversationContext
1509
+ promptConversationContext,
1510
+ input.actor
1505
1511
  );
1506
1512
  return buildUserTurnInput({
1507
1513
  omittedImageAttachmentCount: input.omittedImageAttachmentCount ?? 0,
@@ -1511,7 +1517,7 @@ function buildPromptInput(input) {
1511
1517
  }
1512
1518
  function buildSteeringPiMessage(message) {
1513
1519
  const { userContentParts } = buildUserTurnInput({
1514
- userTurnText: buildUserTurnText(message.text),
1520
+ userTurnText: buildUserTurnText(message.text, void 0, message.actor),
1515
1521
  userAttachments: message.userAttachments,
1516
1522
  omittedImageAttachmentCount: message.omittedImageAttachmentCount ?? 0
1517
1523
  });
@@ -1538,35 +1544,7 @@ function withoutTrailingUncheckpointedUserPrompt(messages, userContentParts) {
1538
1544
  return messages.slice(0, -1);
1539
1545
  }
1540
1546
  function userPromptContentMatches(storedContent, currentContent) {
1541
- if (JSON.stringify(storedContent) === JSON.stringify(currentContent)) {
1542
- return true;
1543
- }
1544
- if (!Array.isArray(storedContent)) {
1545
- return false;
1546
- }
1547
- if (storedContent.length !== currentContent.length) {
1548
- return false;
1549
- }
1550
- return storedContent.every((storedPart, index) => {
1551
- const currentPart = currentContent[index];
1552
- if (index === 0 && currentPart?.type === "text") {
1553
- const legacyTextPart = legacyStoredTextPartSchema.safeParse(storedPart);
1554
- if (legacyTextPart.success) {
1555
- return legacyTextPartMatchesCurrentText(
1556
- legacyTextPart.data.text,
1557
- currentPart.text
1558
- );
1559
- }
1560
- }
1561
- return JSON.stringify(storedPart) === JSON.stringify(currentPart);
1562
- });
1563
- }
1564
- function legacyTextPartMatchesCurrentText(storedText, currentText) {
1565
- const storedInstructionBody = extractCurrentInstructionBody(storedText);
1566
- if (storedInstructionBody !== void 0) {
1567
- return renderCurrentInstruction(storedInstructionBody) === currentText;
1568
- }
1569
- return renderCurrentInstruction(storedText) === currentText;
1547
+ return JSON.stringify(storedContent) === JSON.stringify(currentContent);
1570
1548
  }
1571
1549
  async function assemblePrompt(args) {
1572
1550
  const source = args.routing.source;
@@ -1601,7 +1579,7 @@ async function assemblePrompt(args) {
1601
1579
  source
1602
1580
  } : void 0,
1603
1581
  invocation: args.invocation,
1604
- requester: args.actorRequester,
1582
+ actor: isUserActor(args.currentActor) ? args.currentActor : void 0,
1605
1583
  artifactState: args.artifactState,
1606
1584
  configuration: args.configurationValues
1607
1585
  }) : null;
@@ -1769,7 +1747,7 @@ ${usage}
1769
1747
  const entry = await configuration.set({
1770
1748
  key,
1771
1749
  value,
1772
- updatedBy: deps.requesterId,
1750
+ updatedBy: deps.actorId,
1773
1751
  source: "jr-rpc"
1774
1752
  });
1775
1753
  logInfo(
@@ -2698,30 +2676,30 @@ function serializeMcpPayload(payload, privacy) {
2698
2676
  }
2699
2677
 
2700
2678
  // src/chat/tools/sandbox/bash.ts
2701
- import { z as z5 } from "zod";
2679
+ import { z as z4 } from "zod";
2702
2680
 
2703
2681
  // src/chat/tool-support/structured-result.ts
2704
- import { z as z3 } from "zod";
2705
- var juniorToolContinuationSchema = z3.object({
2706
- arguments: z3.record(z3.string(), z3.unknown()),
2707
- reason: z3.string().min(1).optional()
2682
+ import { z as z2 } from "zod";
2683
+ var juniorToolContinuationSchema = z2.object({
2684
+ arguments: z2.record(z2.string(), z2.unknown()),
2685
+ reason: z2.string().min(1).optional()
2708
2686
  }).strict();
2709
2687
  var juniorToolBoundContinuationSchema = juniorToolContinuationSchema.extend({
2710
- tool_name: z3.string().min(1)
2688
+ tool_name: z2.string().min(1)
2711
2689
  });
2712
- var juniorToolErrorSchema = z3.object({
2713
- kind: z3.string().min(1),
2714
- message: z3.string().min(1),
2715
- retryable: z3.boolean().optional()
2690
+ var juniorToolErrorSchema = z2.object({
2691
+ kind: z2.string().min(1),
2692
+ message: z2.string().min(1),
2693
+ retryable: z2.boolean().optional()
2716
2694
  }).strict();
2717
- var juniorToolResultSchema = z3.object({
2718
- ok: z3.boolean(),
2719
- status: z3.enum(["success", "error"]),
2720
- target: z3.string().min(1).optional(),
2721
- data: z3.unknown().optional(),
2722
- truncated: z3.boolean().optional(),
2695
+ var juniorToolResultSchema = z2.object({
2696
+ ok: z2.boolean(),
2697
+ status: z2.enum(["success", "error"]),
2698
+ target: z2.string().min(1).optional(),
2699
+ data: z2.unknown().optional(),
2700
+ truncated: z2.boolean().optional(),
2723
2701
  continuation: juniorToolContinuationSchema.optional(),
2724
- error: z3.union([juniorToolErrorSchema, z3.string()]).optional()
2702
+ error: z2.union([juniorToolErrorSchema, z2.string()]).optional()
2725
2703
  }).passthrough();
2726
2704
  var juniorToolResultWithBoundContinuationSchema = juniorToolResultSchema.extend({
2727
2705
  continuation: juniorToolBoundContinuationSchema.optional()
@@ -2768,7 +2746,7 @@ function makeStructuredToolResult(details, options = {}) {
2768
2746
  }
2769
2747
 
2770
2748
  // src/chat/tool-support/zod-tool.ts
2771
- import { z as z4 } from "zod";
2749
+ import { z as z3 } from "zod";
2772
2750
 
2773
2751
  // src/chat/tools/execution/tool-input-error.ts
2774
2752
  var ToolInputError = class extends Error {
@@ -2802,7 +2780,7 @@ function zodTool(definition) {
2802
2780
  const { inputSchema, outputSchema, prepareArguments, execute, ...toolDef } = definition;
2803
2781
  let modelInputSchema;
2804
2782
  try {
2805
- modelInputSchema = z4.toJSONSchema(inputSchema);
2783
+ modelInputSchema = z3.toJSONSchema(inputSchema);
2806
2784
  } catch (error) {
2807
2785
  throw new TypeError(
2808
2786
  "zodTool() inputSchema must be representable as JSON Schema.",
@@ -2812,7 +2790,7 @@ function zodTool(definition) {
2812
2790
  let modelOutputSchema;
2813
2791
  if (outputSchema) {
2814
2792
  try {
2815
- modelOutputSchema = z4.toJSONSchema(outputSchema);
2793
+ modelOutputSchema = z3.toJSONSchema(outputSchema);
2816
2794
  } catch (error) {
2817
2795
  throw new TypeError(
2818
2796
  "zodTool() outputSchema must be representable as JSON Schema.",
@@ -2854,9 +2832,9 @@ function zodTool(definition) {
2854
2832
  function createBashTool() {
2855
2833
  return zodTool({
2856
2834
  description: "Run a bash command inside the isolated sandbox workspace. Use this for repository inspection/execution tasks that need shell access. Do not use for network-sensitive or destructive actions unless explicitly required.",
2857
- inputSchema: z5.object({
2858
- command: z5.string().min(1).describe("Bash command to run inside the sandbox."),
2859
- timeoutMs: z5.coerce.number().int().min(1e3).describe(
2835
+ inputSchema: z4.object({
2836
+ command: z4.string().min(1).describe("Bash command to run inside the sandbox."),
2837
+ timeoutMs: z4.coerce.number().int().min(1e3).describe(
2860
2838
  "Optional command timeout in milliseconds. Use for commands that may hang."
2861
2839
  ).optional()
2862
2840
  }),
@@ -3230,7 +3208,7 @@ function validateAndApplyTextEdits(content, edits, targetName) {
3230
3208
  }
3231
3209
 
3232
3210
  // src/chat/tools/sandbox/edit-file.ts
3233
- import { z as z6 } from "zod";
3211
+ import { z as z5 } from "zod";
3234
3212
  function prepareEditFileArguments(input) {
3235
3213
  return prepareTextReplacementArguments(input);
3236
3214
  }
@@ -3328,20 +3306,20 @@ async function editFile(params) {
3328
3306
  replacements: params.edits.length
3329
3307
  });
3330
3308
  }
3331
- var editReplacementSchema = z6.object({
3332
- oldText: z6.string().min(1).describe(
3309
+ var editReplacementSchema = z5.object({
3310
+ oldText: z5.string().min(1).describe(
3333
3311
  "Exact text to replace. It must be unique in the original file and must not overlap another edit."
3334
3312
  ),
3335
- newText: z6.string().describe("Replacement text for this edit.")
3313
+ newText: z5.string().describe("Replacement text for this edit.")
3336
3314
  });
3337
3315
  function createEditFileTool() {
3338
3316
  return zodTool({
3339
3317
  description: "Edit one sandbox workspace file with exact text replacements. Use for precise changes to existing files; prefer this over writeFile for targeted changes. Each oldText must match exactly, be unique, and not overlap another edit. Returns a diff. Multiple changes to the same file: use one edits[] call.",
3340
3318
  prepareArguments: prepareEditFileArguments,
3341
3319
  executionMode: "sequential",
3342
- inputSchema: z6.object({
3343
- path: z6.string().min(1).describe("Path to edit in the sandbox workspace."),
3344
- edits: z6.array(editReplacementSchema).min(1).describe(
3320
+ inputSchema: z5.object({
3321
+ path: z5.string().min(1).describe("Path to edit in the sandbox workspace."),
3322
+ edits: z5.array(editReplacementSchema).min(1).describe(
3345
3323
  "Exact replacements matched against the original file, not incrementally."
3346
3324
  )
3347
3325
  }),
@@ -3356,7 +3334,7 @@ function createEditFileTool() {
3356
3334
 
3357
3335
  // src/chat/tools/sandbox/find-files.ts
3358
3336
  import path3 from "path";
3359
- import { z as z7 } from "zod";
3337
+ import { z as z6 } from "zod";
3360
3338
  var DEFAULT_FIND_LIMIT = 1e3;
3361
3339
  async function findFiles(params) {
3362
3340
  if (!params.pattern.trim()) {
@@ -3416,14 +3394,14 @@ function createFindFilesTool() {
3416
3394
  return zodTool({
3417
3395
  description: "Find sandbox workspace files by glob pattern. Returns bounded paths relative to the search root and skips dependency/cache directories.",
3418
3396
  annotations: { readOnlyHint: true, destructiveHint: false },
3419
- inputSchema: z7.object({
3420
- pattern: z7.string().min(1).describe(
3397
+ inputSchema: z6.object({
3398
+ pattern: z6.string().min(1).describe(
3421
3399
  "Glob pattern to match, for example '*.ts', '**/*.json', or 'src/**/*.test.ts'."
3422
3400
  ),
3423
- path: z7.string().min(1).describe(
3401
+ path: z6.string().min(1).describe(
3424
3402
  "Directory or file path in the sandbox workspace. Defaults to the workspace root."
3425
3403
  ).optional(),
3426
- limit: z7.coerce.number().int().min(1).describe("Maximum number of file paths to return. Defaults to 1000.").optional()
3404
+ limit: z6.coerce.number().int().min(1).describe("Maximum number of file paths to return. Defaults to 1000.").optional()
3427
3405
  }),
3428
3406
  outputSchema: juniorToolResultSchema,
3429
3407
  execute: async () => {
@@ -3436,12 +3414,12 @@ function createFindFilesTool() {
3436
3414
 
3437
3415
  // src/chat/tools/sandbox/grep.ts
3438
3416
  import path4 from "path";
3439
- import { z as z8 } from "zod";
3417
+ import { z as z7 } from "zod";
3440
3418
  var DEFAULT_GREP_LIMIT = 100;
3441
3419
  var MAX_GREP_LINE_CHARS = 500;
3442
- var booleanInput = (description) => z8.preprocess(
3420
+ var booleanInput = (description) => z7.preprocess(
3443
3421
  (value) => value === "true" ? true : value === "false" ? false : value,
3444
- z8.boolean()
3422
+ z7.boolean()
3445
3423
  ).describe(description);
3446
3424
  function truncateGrepLine(value) {
3447
3425
  if (value.length <= MAX_GREP_LINE_CHARS) {
@@ -3590,20 +3568,20 @@ function createGrepTool() {
3590
3568
  return zodTool({
3591
3569
  description: "Search sandbox workspace file contents. Returns bounded matching lines with file paths and line numbers. Respects path/glob filters and includes truncation notices.",
3592
3570
  annotations: { readOnlyHint: true, destructiveHint: false },
3593
- inputSchema: z8.object({
3594
- pattern: z8.string().min(1).describe("Regex pattern or literal text to search for."),
3595
- path: z8.string().min(1).describe(
3571
+ inputSchema: z7.object({
3572
+ pattern: z7.string().min(1).describe("Regex pattern or literal text to search for."),
3573
+ path: z7.string().min(1).describe(
3596
3574
  "Directory or file path in the sandbox workspace. Defaults to the workspace root."
3597
3575
  ).optional(),
3598
- glob: z8.string().min(1).describe("Optional glob filter such as '*.ts' or '**/*.test.ts'.").optional(),
3576
+ glob: z7.string().min(1).describe("Optional glob filter such as '*.ts' or '**/*.test.ts'.").optional(),
3599
3577
  ignoreCase: booleanInput(
3600
3578
  "Whether matching should ignore case."
3601
3579
  ).optional(),
3602
3580
  literal: booleanInput(
3603
3581
  "Treat pattern as literal text instead of regex."
3604
3582
  ).optional(),
3605
- context: z8.coerce.number().int().min(0).describe("Number of surrounding context lines to include.").optional(),
3606
- limit: z8.coerce.number().int().min(1).describe("Maximum matches to return. Defaults to 100.").optional()
3583
+ context: z7.coerce.number().int().min(0).describe("Number of surrounding context lines to include.").optional(),
3584
+ limit: z7.coerce.number().int().min(1).describe("Maximum matches to return. Defaults to 100.").optional()
3607
3585
  }),
3608
3586
  outputSchema: juniorToolResultSchema,
3609
3587
  execute: async () => {
@@ -3721,7 +3699,7 @@ async function readSandboxFileUpload(sandbox, input) {
3721
3699
 
3722
3700
  // src/chat/tools/sandbox/list-dir.ts
3723
3701
  import path6 from "path";
3724
- import { z as z9 } from "zod";
3702
+ import { z as z8 } from "zod";
3725
3703
  var DEFAULT_LIST_LIMIT = 500;
3726
3704
  async function listDir(params) {
3727
3705
  const dirPath = resolveWorkspacePath(params.path);
@@ -3802,11 +3780,11 @@ function createListDirTool() {
3802
3780
  return zodTool({
3803
3781
  description: "List a sandbox workspace directory. Returns sorted entries with '/' suffixes for directories and bounded truncation notices.",
3804
3782
  annotations: { readOnlyHint: true, destructiveHint: false },
3805
- inputSchema: z9.object({
3806
- path: z9.string().min(1).describe(
3783
+ inputSchema: z8.object({
3784
+ path: z8.string().min(1).describe(
3807
3785
  "Directory path in the sandbox workspace. Defaults to the workspace root."
3808
3786
  ).optional(),
3809
- limit: z9.coerce.number().int().min(1).describe("Maximum entries to return. Defaults to 500.").optional()
3787
+ limit: z8.coerce.number().int().min(1).describe("Maximum entries to return. Defaults to 500.").optional()
3810
3788
  }),
3811
3789
  outputSchema: juniorToolResultSchema,
3812
3790
  execute: async () => {
@@ -3818,8 +3796,24 @@ function createListDirTool() {
3818
3796
  }
3819
3797
 
3820
3798
  // src/chat/tools/web/image-generate.ts
3821
- import { z as z10 } from "zod";
3799
+ import { z as z9 } from "zod";
3822
3800
  var DEFAULT_IMAGE_MODEL = "google/gemini-3-pro-image";
3801
+ var imageGenerateOutputSchema = juniorToolResultSchema.extend({
3802
+ model: z9.string(),
3803
+ prompt: z9.string(),
3804
+ enrichedPrompt: z9.string(),
3805
+ image_count: z9.number().int().nonnegative(),
3806
+ images: z9.array(
3807
+ z9.object({
3808
+ filename: z9.string(),
3809
+ path: z9.string(),
3810
+ attachment_path: z9.string(),
3811
+ media_type: z9.string().optional(),
3812
+ bytes: z9.number().int().nonnegative()
3813
+ }).strict()
3814
+ ),
3815
+ delivery: z9.string()
3816
+ }).strict();
3823
3817
  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.
3824
3818
 
3825
3819
  <personality>
@@ -3879,10 +3873,10 @@ function parseImageGenerationError(status, body, model) {
3879
3873
  function createImageGenerateTool(hooks, options = {}, deps = {}) {
3880
3874
  return zodTool({
3881
3875
  description: "Generate images from a prompt. Use when the user wants to visually show or represent something \u2014 feelings, concepts, art, humor, or any visual idea. Also use for explicit image creation requests.",
3882
- inputSchema: z10.object({
3883
- prompt: z10.string().min(1).max(4e3).describe("Image generation prompt.")
3876
+ inputSchema: z9.object({
3877
+ prompt: z9.string().min(1).max(4e3).describe("Image generation prompt.")
3884
3878
  }),
3885
- outputSchema: juniorToolResultSchema,
3879
+ outputSchema: imageGenerateOutputSchema,
3886
3880
  execute: async ({ prompt }) => {
3887
3881
  const fetchImpl = deps.fetch ?? fetch;
3888
3882
  const apiKey = getGatewayApiKey();
@@ -3972,7 +3966,7 @@ function createImageGenerateTool(hooks, options = {}, deps = {}) {
3972
3966
  }
3973
3967
 
3974
3968
  // src/chat/tools/skill/call-mcp-tool.ts
3975
- import { z as z11 } from "zod";
3969
+ import { z as z10 } from "zod";
3976
3970
  function resolveMcpArguments(input) {
3977
3971
  const extraKeys = Object.keys(input).filter(
3978
3972
  (key) => key !== "tool_name" && key !== "arguments"
@@ -4008,9 +4002,9 @@ function missingToolMessage(toolName, provider) {
4008
4002
  function createCallMcpToolTool(mcpToolManager) {
4009
4003
  return zodTool({
4010
4004
  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.",
4011
- inputSchema: z11.object({
4012
- tool_name: z11.string().min(1).describe("Exact MCP tool_name from searchMcpTools."),
4013
- arguments: z11.unknown().describe(
4005
+ inputSchema: z10.object({
4006
+ tool_name: z10.string().min(1).describe("Exact MCP tool_name from searchMcpTools."),
4007
+ arguments: z10.record(z10.string(), z10.unknown()).describe(
4014
4008
  'Arguments matching the disclosed MCP tool schema, for example { "query": "..." } when searchMcpTools shows query is required.'
4015
4009
  ).optional()
4016
4010
  }).passthrough(),
@@ -4051,7 +4045,7 @@ function createCallMcpToolTool(mcpToolManager) {
4051
4045
  }
4052
4046
 
4053
4047
  // src/chat/tools/skill/load-skill.ts
4054
- import { z as z12 } from "zod";
4048
+ import { z as z11 } from "zod";
4055
4049
  function toLoadedSkill(result, availableSkills) {
4056
4050
  if (result.ok !== true || typeof result.skill_name !== "string" || typeof result.description !== "string" || typeof result.skill_dir !== "string" || typeof result.instructions !== "string") {
4057
4051
  return null;
@@ -4100,8 +4094,8 @@ async function loadSkillFromHost(availableSkills, skillName) {
4100
4094
  function createLoadSkillTool(availableSkills, options) {
4101
4095
  return zodTool({
4102
4096
  description: "Load a skill by name for this turn. The result includes working_directory; resolve skill paths there and run skill-owned bash commands from there or with absolute paths. When the result includes mcp_provider, use searchMcpTools before callMcpTool. Use when a request clearly matches a known skill.",
4103
- inputSchema: z12.object({
4104
- skill_name: z12.string().min(1).describe("Skill name to load, without the leading slash.")
4097
+ inputSchema: z11.object({
4098
+ skill_name: z11.string().min(1).describe("Skill name to load, without the leading slash.")
4105
4099
  }),
4106
4100
  outputSchema: juniorToolResultSchema,
4107
4101
  execute: async ({ skill_name }) => {
@@ -4119,7 +4113,7 @@ function createLoadSkillTool(availableSkills, options) {
4119
4113
  }
4120
4114
 
4121
4115
  // src/chat/tools/skill/search-mcp-tools.ts
4122
- import { z as z13 } from "zod";
4116
+ import { z as z12 } from "zod";
4123
4117
 
4124
4118
  // src/chat/tool-support/schema-summary.ts
4125
4119
  function getSchemaProperties(schema) {
@@ -4249,6 +4243,38 @@ function toActiveMcpCatalogSummaries(toolDefs) {
4249
4243
  // src/chat/tools/skill/search-mcp-tools.ts
4250
4244
  var DEFAULT_MAX_RESULTS = 5;
4251
4245
  var MAX_RESULTS = 20;
4246
+ var providerSummarySchema = z12.object({
4247
+ provider: z12.string(),
4248
+ description: z12.string(),
4249
+ active: z12.boolean()
4250
+ }).strict();
4251
+ var mcpCallExampleSchema = z12.object({
4252
+ tool_name: z12.string(),
4253
+ arguments: z12.record(z12.string(), z12.string())
4254
+ }).strict();
4255
+ var exposedToolSummarySchema = z12.object({
4256
+ tool_name: z12.string(),
4257
+ mcp_tool_name: z12.string(),
4258
+ provider: z12.string(),
4259
+ title: z12.string().optional(),
4260
+ description: z12.string(),
4261
+ signature: z12.string(),
4262
+ call: mcpCallExampleSchema,
4263
+ input_schema: z12.record(z12.string(), z12.unknown()),
4264
+ input_schema_summary: z12.string(),
4265
+ output_schema: z12.record(z12.string(), z12.unknown()).optional(),
4266
+ annotations: z12.record(z12.string(), z12.unknown()).optional()
4267
+ }).strict();
4268
+ var searchMcpToolsOutputSchema = juniorToolResultSchema.extend({
4269
+ query: z12.string().nullable(),
4270
+ provider: z12.string().nullable(),
4271
+ total_active_tools: z12.number().int().nonnegative(),
4272
+ returned_tools: z12.number().int().nonnegative(),
4273
+ execution_tool: z12.literal("callMcpTool"),
4274
+ execution_example: mcpCallExampleSchema,
4275
+ available_providers: z12.array(providerSummarySchema),
4276
+ tools: z12.array(exposedToolSummarySchema)
4277
+ }).strict();
4252
4278
  function normalize(value) {
4253
4279
  return value.toLowerCase().replace(/[^a-z0-9_]+/g, " ").trim();
4254
4280
  }
@@ -4367,16 +4393,16 @@ function searchProviderCatalog(providers, query) {
4367
4393
  function createSearchMcpToolsTool(mcpToolManager) {
4368
4394
  return zodTool({
4369
4395
  description: "List or search MCP providers and active MCP tools. When provider is supplied and not yet active, Junior connects to it on demand and returns tool descriptors including schemas. Without provider, returns active tools plus matching configured providers without connecting. Use when choosing a provider tool or when callMcpTool arguments are unclear.",
4370
- inputSchema: z13.object({
4371
- query: z13.string().min(1).describe(
4396
+ inputSchema: z12.object({
4397
+ query: z12.string().min(1).describe(
4372
4398
  "Optional search terms describing the MCP tool or arguments needed."
4373
4399
  ).optional(),
4374
- provider: z13.string().min(1).describe(
4400
+ provider: z12.string().min(1).describe(
4375
4401
  "Optional provider name to list or search within. If configured but not yet connected, Junior activates it on demand."
4376
4402
  ).optional(),
4377
- max_results: z13.number().int().min(1).max(MAX_RESULTS).describe("Maximum matching tool descriptors to return.").optional()
4403
+ max_results: z12.number().int().min(1).max(MAX_RESULTS).describe("Maximum matching tool descriptors to return.").optional()
4378
4404
  }).strict(),
4379
- outputSchema: juniorToolResultSchema,
4405
+ outputSchema: searchMcpToolsOutputSchema,
4380
4406
  execute: async ({ query, provider, max_results }) => {
4381
4407
  if (provider) {
4382
4408
  await mcpToolManager.activateProvider(provider);
@@ -4398,6 +4424,13 @@ function createSearchMcpToolsTool(mcpToolManager) {
4398
4424
  provider: provider ?? null,
4399
4425
  total_active_tools: catalog.length,
4400
4426
  returned_tools: matches.length,
4427
+ execution_tool: "callMcpTool",
4428
+ execution_example: {
4429
+ tool_name: "<returned tool_name>",
4430
+ arguments: {
4431
+ "<argument>": "<value from input_schema>"
4432
+ }
4433
+ },
4401
4434
  available_providers: providers,
4402
4435
  tools: matches.map(toExposedToolSummary)
4403
4436
  };
@@ -4412,7 +4445,7 @@ function createSearchMcpToolsTool(mcpToolManager) {
4412
4445
  }
4413
4446
 
4414
4447
  // src/chat/tools/sandbox/read-file.ts
4415
- import { z as z14 } from "zod";
4448
+ import { z as z13 } from "zod";
4416
4449
 
4417
4450
  // src/chat/tool-support/text-range-result.ts
4418
4451
  var DEFAULT_READ_LIMIT = 1e3;
@@ -4487,10 +4520,10 @@ function createReadFileTool() {
4487
4520
  return zodTool({
4488
4521
  description: "Read a bounded line range from a file in the sandbox workspace. Use when you need exact file contents to verify facts or make edits safely. Prefer grep/findFiles/listDir for broad discovery.",
4489
4522
  annotations: { readOnlyHint: true, destructiveHint: false },
4490
- inputSchema: z14.object({
4491
- path: z14.string().min(1).describe("Path to the file in the sandbox workspace."),
4492
- offset: z14.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
4493
- limit: z14.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
4523
+ inputSchema: z13.object({
4524
+ path: z13.string().min(1).describe("Path to the file in the sandbox workspace."),
4525
+ offset: z13.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
4526
+ limit: z13.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
4494
4527
  }),
4495
4528
  outputSchema: juniorToolResultSchema,
4496
4529
  execute: async () => {
@@ -4502,12 +4535,12 @@ function createReadFileTool() {
4502
4535
  }
4503
4536
 
4504
4537
  // src/chat/tools/runtime/report-progress.ts
4505
- import { z as z15 } from "zod";
4538
+ import { z as z14 } from "zod";
4506
4539
  function createReportProgressTool() {
4507
4540
  return zodTool({
4508
4541
  description: "Update the user-visible assistant loading message with a short progress phase. For every non-trivial turn, call this early with the initial major work phase, then call it again only when the major phase meaningfully changes. Messages must be written in sentence case with a present-participle verb (e.g. 'Searching docs', 'Reviewing results', 'Running checks'). Skip trivial direct answers, generic filler, and minor substeps.",
4509
- inputSchema: z15.object({
4510
- message: z15.string().min(1).describe("Short user-facing progress message.")
4542
+ inputSchema: z14.object({
4543
+ message: z14.string().min(1).describe("Short user-facing progress message.")
4511
4544
  }),
4512
4545
  outputSchema: juniorToolResultSchema,
4513
4546
  execute: async () => ({
@@ -4518,31 +4551,31 @@ function createReportProgressTool() {
4518
4551
  }
4519
4552
 
4520
4553
  // src/chat/tools/resource-events.ts
4521
- import { z as z17 } from "zod";
4554
+ import { z as z16 } from "zod";
4522
4555
 
4523
4556
  // src/chat/resource-events/store.ts
4524
4557
  import { createHash } from "crypto";
4525
4558
  import { destinationSchema } from "@sentry/junior-plugin-api";
4526
- import { z as z16 } from "zod";
4559
+ import { z as z15 } from "zod";
4527
4560
  var RESOURCE_EVENT_PREFIX = "junior:resource_event_subscription";
4528
4561
  var INDEX_LOCK_TTL_MS = 1e4;
4529
4562
  var SUBSCRIPTION_LOCK_TTL_MS = 1e4;
4530
- var subscriptionStatusSchema = z16.enum(["active", "cancelled", "completed"]);
4531
- var subscriptionIdIndexSchema = z16.array(z16.string().min(1));
4532
- var subscriptionSchema = z16.object({
4533
- conversationId: z16.string().min(1),
4534
- createdAtMs: z16.number().finite(),
4563
+ var subscriptionStatusSchema = z15.enum(["active", "cancelled", "completed"]);
4564
+ var subscriptionIdIndexSchema = z15.array(z15.string().min(1));
4565
+ var subscriptionSchema = z15.object({
4566
+ conversationId: z15.string().min(1),
4567
+ createdAtMs: z15.number().finite(),
4535
4568
  destination: destinationSchema,
4536
- events: z16.array(z16.string().min(1)).min(1),
4537
- expiresAtMs: z16.number().finite(),
4538
- id: z16.string().min(1),
4539
- intent: z16.string().min(1),
4540
- label: z16.string().min(1),
4541
- provider: z16.string().min(1),
4542
- resourceRef: z16.string().min(1),
4543
- resourceType: z16.string().min(1),
4569
+ events: z15.array(z15.string().min(1)).min(1),
4570
+ expiresAtMs: z15.number().finite(),
4571
+ id: z15.string().min(1),
4572
+ intent: z15.string().min(1),
4573
+ label: z15.string().min(1),
4574
+ provider: z15.string().min(1),
4575
+ resourceRef: z15.string().min(1),
4576
+ resourceType: z15.string().min(1),
4544
4577
  status: subscriptionStatusSchema,
4545
- updatedAtMs: z16.number().finite()
4578
+ updatedAtMs: z15.number().finite()
4546
4579
  }).strict();
4547
4580
  function digest(value) {
4548
4581
  return createHash("sha256").update(value).digest("hex").slice(0, 32);
@@ -4832,23 +4865,23 @@ async function deliverResourceEventSubscription(input) {
4832
4865
  // src/chat/tools/resource-events.ts
4833
4866
  var DEFAULT_TTL_MS = 14 * 24 * 60 * 60 * 1e3;
4834
4867
  var MAX_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
4835
- var subscribeInputSchema = z17.object({
4836
- resourceRef: z17.string().describe("Opaque resource ref copied from a subscribable tool result."),
4837
- provider: z17.string().describe("Provider that owns the resource ref."),
4838
- resourceType: z17.string().describe("Provider-defined resource type from the subscribable hint."),
4839
- label: z17.string().describe("Human-readable resource label from the subscribable hint."),
4840
- events: z17.array(z17.string()).min(1).describe(
4868
+ var subscribeInputSchema = z16.object({
4869
+ resourceRef: z16.string().describe("Opaque resource ref copied from a subscribable tool result."),
4870
+ provider: z16.string().describe("Provider that owns the resource ref."),
4871
+ resourceType: z16.string().describe("Provider-defined resource type from the subscribable hint."),
4872
+ label: z16.string().describe("Human-readable resource label from the subscribable hint."),
4873
+ events: z16.array(z16.string()).min(1).describe(
4841
4874
  "High-signal event names to deliver to this conversation when they occur."
4842
4875
  ),
4843
- intent: z17.string().describe(
4876
+ intent: z16.string().describe(
4844
4877
  "Concise reason this conversation wants these events, used when an event arrives."
4845
4878
  ),
4846
- ttlMs: z17.coerce.number().describe(
4879
+ ttlMs: z16.coerce.number().describe(
4847
4880
  "How long to keep the subscription active. Defaults to 14 days and is capped at 30 days."
4848
4881
  ).optional()
4849
4882
  });
4850
- var cancelInputSchema = z17.object({
4851
- subscriptionId: z17.string().describe(
4883
+ var cancelInputSchema = z16.object({
4884
+ subscriptionId: z16.string().describe(
4852
4885
  "Subscription id returned by subscribeToResourceEvents or listResourceEventSubscriptions."
4853
4886
  )
4854
4887
  });
@@ -4932,7 +4965,7 @@ function createSubscribeToResourceEventsTool(context) {
4932
4965
  function createListResourceEventSubscriptionsTool(context) {
4933
4966
  return zodTool({
4934
4967
  description: "List active resource event subscriptions for the current conversation.",
4935
- inputSchema: z17.object({}),
4968
+ inputSchema: z16.object({}),
4936
4969
  outputSchema: juniorToolResultSchema,
4937
4970
  async execute() {
4938
4971
  const conversationId = requireConversationContext(context);
@@ -5067,11 +5100,11 @@ async function listThreadReplies(input) {
5067
5100
  }
5068
5101
 
5069
5102
  // src/chat/slack/timestamp-param.ts
5070
- import { z as z18 } from "zod";
5103
+ import { z as z17 } from "zod";
5071
5104
  function slackTimestampParam(description) {
5072
- return z18.preprocess(
5105
+ return z17.preprocess(
5073
5106
  (value) => typeof value === "number" && Number.isFinite(value) ? String(value) : value,
5074
- z18.string().min(1)
5107
+ z17.string().min(1)
5075
5108
  ).describe(description);
5076
5109
  }
5077
5110
  function optionalSlackTimestampParam(description) {
@@ -5102,10 +5135,10 @@ function parseRequiredSlackTimestampParam(field, value) {
5102
5135
  }
5103
5136
 
5104
5137
  // src/chat/slack/tools/channel-list-messages.ts
5105
- import { z as z19 } from "zod";
5106
- var booleanInput2 = (description) => z19.preprocess(
5138
+ import { z as z18 } from "zod";
5139
+ var booleanInput2 = (description) => z18.preprocess(
5107
5140
  (value) => value === "true" ? true : value === "false" ? false : value,
5108
- z19.boolean()
5141
+ z18.boolean()
5109
5142
  ).describe(description);
5110
5143
  function normalizeRangeTimestamp(field, value, targetChannelId) {
5111
5144
  if (value === void 0) {
@@ -5132,9 +5165,9 @@ function createSlackChannelListMessagesTool(context) {
5132
5165
  return zodTool({
5133
5166
  description: "List channel messages from Slack history in the active channel context. Use when the user asks for recent or historical channel context outside this thread. Do not use for live monitoring or when current thread context already answers the question.",
5134
5167
  annotations: { readOnlyHint: true, destructiveHint: false },
5135
- inputSchema: z19.object({
5136
- limit: z19.coerce.number().int().min(1).max(1e3).describe("Maximum number of messages to return across pages.").optional(),
5137
- cursor: z19.string().min(1).describe("Optional cursor to continue from a prior call.").optional(),
5168
+ inputSchema: z18.object({
5169
+ limit: z18.coerce.number().int().min(1).max(1e3).describe("Maximum number of messages to return across pages.").optional(),
5170
+ cursor: z18.string().min(1).describe("Optional cursor to continue from a prior call.").optional(),
5138
5171
  oldest: optionalSlackTimestampParam(
5139
5172
  "Optional oldest message timestamp (Slack ts) for range filtering."
5140
5173
  ),
@@ -5144,7 +5177,7 @@ function createSlackChannelListMessagesTool(context) {
5144
5177
  inclusive: booleanInput2(
5145
5178
  "Whether oldest/latest bounds should be inclusive."
5146
5179
  ).optional(),
5147
- max_pages: z19.coerce.number().int().min(1).max(10).describe("Maximum number of API pages to traverse in a single call.").optional()
5180
+ max_pages: z18.coerce.number().int().min(1).max(10).describe("Maximum number of API pages to traverse in a single call.").optional()
5148
5181
  }),
5149
5182
  outputSchema: juniorToolResultSchema,
5150
5183
  execute: async ({
@@ -5477,7 +5510,7 @@ async function removeReactionFromMessage(input) {
5477
5510
  }
5478
5511
 
5479
5512
  // src/chat/slack/tools/message-add-reaction.ts
5480
- import { z as z20 } from "zod";
5513
+ import { z as z19 } from "zod";
5481
5514
 
5482
5515
  // src/chat/tools/idempotency.ts
5483
5516
  function stableSerialize(value) {
@@ -5499,8 +5532,8 @@ function createOperationKey(toolName, input) {
5499
5532
  function createSlackMessageAddReactionTool(context, state) {
5500
5533
  return zodTool({
5501
5534
  description: "Add an emoji reaction to the current inbound Slack message. Use when the user asks for a reaction on the current message without another target. Provide a Slack emoji alias name (for example `thumbsup`, `white_check_mark`, or `thumbsup::skin-tone-6`), not a unicode emoji glyph. The target message is injected by runtime context; do not use this for arbitrary historical messages.",
5502
- inputSchema: z20.object({
5503
- emoji: z20.string().min(1).max(64).describe(
5535
+ inputSchema: z19.object({
5536
+ emoji: z19.string().min(1).max(64).describe(
5504
5537
  "Slack emoji alias name to react with (for example `thumbsup`, `white_check_mark`, or `thumbsup::skin-tone-6`). Optional surrounding colons are allowed."
5505
5538
  )
5506
5539
  }),
@@ -5551,15 +5584,15 @@ function createSlackMessageAddReactionTool(context, state) {
5551
5584
 
5552
5585
  // src/chat/slack/tools/send-message.ts
5553
5586
  import { createHash as createHash2 } from "crypto";
5554
- import { z as z21 } from "zod";
5555
- var fileInputSchema = z21.object({
5556
- path: z21.string().min(1).describe(
5587
+ import { z as z20 } from "zod";
5588
+ var fileInputSchema = z20.object({
5589
+ path: z20.string().min(1).describe(
5557
5590
  "Sandbox file path to include in the message. Absolute paths and workspace-relative paths are supported."
5558
5591
  ),
5559
- filename: z21.string().min(1).nullable().optional().describe(
5592
+ filename: z20.string().min(1).nullable().optional().describe(
5560
5593
  "Optional filename override shown in Slack. Null is treated as omitted."
5561
5594
  ),
5562
- mimeType: z21.string().min(1).nullable().optional().describe("Optional MIME type override. Null is treated as omitted.")
5595
+ mimeType: z20.string().min(1).nullable().optional().describe("Optional MIME type override. Null is treated as omitted.")
5563
5596
  });
5564
5597
  function hasText(text) {
5565
5598
  return typeof text === "string" && text.trim().length > 0;
@@ -5586,9 +5619,9 @@ function fileOperationInput(files) {
5586
5619
  function createSendMessageTool(context, state, materializeFile) {
5587
5620
  return zodTool({
5588
5621
  description: "Send a Slack message with optional files into the active Slack conversation. Use when the user asks to attach, send, or share files here, in this conversation, or in this thread. The message can contain text, files, or both; file-only messages are allowed. Do not use for top-level channel posts, other named channels, inline @mentions, or pinging mentioned users.",
5589
- inputSchema: z21.object({
5590
- text: z21.string().max(4e4).nullable().optional().describe("Slack mrkdwn text to send. Null is treated as omitted."),
5591
- files: z21.array(fileInputSchema).min(1).nullable().optional().describe(
5622
+ inputSchema: z20.object({
5623
+ text: z20.string().max(4e4).nullable().optional().describe("Slack mrkdwn text to send. Null is treated as omitted."),
5624
+ files: z20.array(fileInputSchema).min(1).nullable().optional().describe(
5592
5625
  "Sandbox files to include in the message. Null is treated as omitted."
5593
5626
  )
5594
5627
  }),
@@ -5916,13 +5949,13 @@ function storedCanvasUrl(state, canvasId) {
5916
5949
  }
5917
5950
 
5918
5951
  // src/chat/slack/tools/canvas/create.ts
5919
- import { z as z22 } from "zod";
5952
+ import { z as z21 } from "zod";
5920
5953
  function createSlackCanvasCreateTool(context, state) {
5921
5954
  return zodTool({
5922
5955
  description: "Create a Slack canvas for long-form output in the active assistant context channel. Use when the answer is better as a reusable document than a thread reply: long-form research, timelines, bios/profiles, structured notes, plans, comparisons, or anything likely to exceed one compact Slack reply. After creating it, reply with one or two short sentences plus the canvas link; do not recap the canvas contents. Do not use for short answers that fit cleanly in one normal thread reply.",
5923
- inputSchema: z22.object({
5924
- title: z22.string().min(1).max(160).describe("Canvas title."),
5925
- markdown: z22.string().min(1).describe("Canvas markdown body content.")
5956
+ inputSchema: z21.object({
5957
+ title: z21.string().min(1).max(160).describe("Canvas title."),
5958
+ markdown: z21.string().min(1).describe("Canvas markdown body content.")
5926
5959
  }),
5927
5960
  outputSchema: juniorToolResultSchema,
5928
5961
  execute: async ({ title, markdown }) => {
@@ -5985,28 +6018,39 @@ function createSlackCanvasCreateTool(context, state) {
5985
6018
  }
5986
6019
 
5987
6020
  // src/chat/slack/tools/canvas/edit.ts
5988
- import { z as z23 } from "zod";
6021
+ import { z as z22 } from "zod";
5989
6022
  function prepareCanvasEditArguments(input) {
5990
6023
  return prepareTextReplacementArguments(input);
5991
6024
  }
5992
- var editReplacementSchema2 = z23.object({
5993
- oldText: z23.string().min(1).describe(
6025
+ var editReplacementSchema2 = z22.object({
6026
+ oldText: z22.string().min(1).describe(
5994
6027
  "Exact Canvas markdown to replace. It must be unique in the current Canvas body and must not overlap another edit."
5995
6028
  ),
5996
- newText: z23.string().describe("Replacement Canvas markdown for this edit.")
6029
+ newText: z22.string().describe("Replacement Canvas markdown for this edit.")
5997
6030
  });
6031
+ var slackCanvasEditOutputSchema = juniorToolResultSchema.extend({
6032
+ canvas_id: z22.string().optional(),
6033
+ title: z22.string().optional(),
6034
+ permalink: z22.string().optional(),
6035
+ diff: z22.string().optional(),
6036
+ first_changed_line: z22.number().int().positive().optional(),
6037
+ replacements: z22.number().int().nonnegative().optional(),
6038
+ normalized_heading_count: z22.number().int().nonnegative().optional(),
6039
+ summary: z22.string().optional(),
6040
+ deduplicated: z22.boolean().optional()
6041
+ }).strict();
5998
6042
  function createSlackCanvasEditTool(state) {
5999
6043
  return zodTool({
6000
6044
  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.",
6001
6045
  prepareArguments: prepareCanvasEditArguments,
6002
6046
  executionMode: "sequential",
6003
- inputSchema: z23.object({
6004
- canvas: z23.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
6005
- edits: z23.array(editReplacementSchema2).min(1).describe(
6047
+ inputSchema: z22.object({
6048
+ canvas: z22.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
6049
+ edits: z22.array(editReplacementSchema2).min(1).describe(
6006
6050
  "Exact replacements matched against the current Canvas body, not incrementally."
6007
6051
  )
6008
6052
  }),
6009
- outputSchema: juniorToolResultSchema,
6053
+ outputSchema: slackCanvasEditOutputSchema,
6010
6054
  execute: async ({ canvas, edits }) => {
6011
6055
  const target = resolveCanvasTarget(canvas);
6012
6056
  if (!target.ok) {
@@ -6080,19 +6124,31 @@ function createSlackCanvasEditTool(state) {
6080
6124
  }
6081
6125
 
6082
6126
  // src/chat/slack/tools/canvas/read.ts
6083
- import { z as z24 } from "zod";
6127
+ import { z as z23 } from "zod";
6128
+ var slackCanvasReadOutputSchema = juniorToolResultSchema.extend({
6129
+ canvas_id: z23.string().optional(),
6130
+ title: z23.string().optional(),
6131
+ permalink: z23.string().optional(),
6132
+ mimetype: z23.string().optional(),
6133
+ filetype: z23.string().optional(),
6134
+ original_byte_length: z23.number().int().nonnegative().optional(),
6135
+ content: z23.string().optional(),
6136
+ start_line: z23.number().int().positive().optional(),
6137
+ end_line: z23.number().int().nonnegative().optional(),
6138
+ total_lines: z23.number().int().nonnegative().optional()
6139
+ }).strict();
6084
6140
  function createSlackCanvasReadTool() {
6085
6141
  return zodTool({
6086
6142
  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.",
6087
6143
  annotations: { readOnlyHint: true, destructiveHint: false },
6088
- inputSchema: z24.object({
6089
- canvas: z24.string().min(1).describe(
6144
+ inputSchema: z23.object({
6145
+ canvas: z23.string().min(1).describe(
6090
6146
  "Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL (e.g. `https://team.slack.com/docs/T.../F...`)."
6091
6147
  ),
6092
- offset: z24.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
6093
- limit: z24.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
6148
+ offset: z23.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
6149
+ limit: z23.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
6094
6150
  }),
6095
- outputSchema: juniorToolResultSchema,
6151
+ outputSchema: slackCanvasReadOutputSchema,
6096
6152
  execute: async ({ canvas, offset, limit }) => {
6097
6153
  const target = resolveCanvasTarget(canvas);
6098
6154
  if (!target.ok) {
@@ -6147,14 +6203,14 @@ function createSlackCanvasReadTool() {
6147
6203
  }
6148
6204
 
6149
6205
  // src/chat/slack/tools/canvas/write.ts
6150
- import { z as z25 } from "zod";
6206
+ import { z as z24 } from "zod";
6151
6207
  function createSlackCanvasWriteTool(state) {
6152
6208
  return zodTool({
6153
6209
  description: "Write UTF-8 markdown content to a Slack canvas. Use for deliberate full-Canvas replacement after validation; use slackCanvasEdit for targeted changes to existing canvas content.",
6154
6210
  executionMode: "sequential",
6155
- inputSchema: z25.object({
6156
- canvas: z25.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
6157
- content: z25.string().describe("UTF-8 markdown content to write.")
6211
+ inputSchema: z24.object({
6212
+ canvas: z24.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
6213
+ content: z24.string().describe("UTF-8 markdown content to write.")
6158
6214
  }),
6159
6215
  outputSchema: juniorToolResultSchema,
6160
6216
  execute: async ({ canvas, content }) => {
@@ -6390,12 +6446,12 @@ async function updateListItem(input) {
6390
6446
  }
6391
6447
 
6392
6448
  // src/chat/slack/id-param.ts
6393
- import { z as z26 } from "zod";
6449
+ import { z as z25 } from "zod";
6394
6450
  function slackChannelIdParam(description) {
6395
- return z26.string().min(1).describe(description);
6451
+ return z25.string().min(1).describe(description);
6396
6452
  }
6397
6453
  function slackUserIdParam(description) {
6398
- return z26.string().min(1).describe(description);
6454
+ return z25.string().min(1).describe(description);
6399
6455
  }
6400
6456
  function parseRequiredSlackChannelIdParam(field, value) {
6401
6457
  const channelId = parseSlackChannelId(value);
@@ -6419,16 +6475,16 @@ function parseRequiredSlackUserIdParam(field, value) {
6419
6475
  }
6420
6476
 
6421
6477
  // src/chat/slack/tools/list/add-items.ts
6422
- import { z as z27 } from "zod";
6478
+ import { z as z26 } from "zod";
6423
6479
  function createSlackListAddItemsTool(state) {
6424
6480
  return zodTool({
6425
6481
  description: "Add tasks to the active Slack list tracked in artifact context. Use when the user wants actionable items recorded in the current thread list. Do not use when no list exists and list creation was not requested.",
6426
- inputSchema: z27.object({
6427
- items: z27.array(z27.string().min(1)).min(1).max(25).describe("List item titles to create."),
6482
+ inputSchema: z26.object({
6483
+ items: z26.array(z26.string().min(1)).min(1).max(25).describe("List item titles to create."),
6428
6484
  assignee_user_id: slackUserIdParam(
6429
6485
  "Optional Slack user ID assigned to all created items."
6430
6486
  ).optional(),
6431
- due_date: z27.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Optional due date in YYYY-MM-DD format.").optional()
6487
+ due_date: z26.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Optional due date in YYYY-MM-DD format.").optional()
6432
6488
  }),
6433
6489
  outputSchema: juniorToolResultSchema,
6434
6490
  execute: async ({ items, assignee_user_id, due_date }) => {
@@ -6482,12 +6538,12 @@ function createSlackListAddItemsTool(state) {
6482
6538
  }
6483
6539
 
6484
6540
  // src/chat/slack/tools/list/create.ts
6485
- import { z as z28 } from "zod";
6541
+ import { z as z27 } from "zod";
6486
6542
  function createSlackListCreateTool(state) {
6487
6543
  return zodTool({
6488
6544
  description: "Create a Slack todo list for action tracking. Use when the user needs structured tasks with ownership/completion tracking. Do not use for one-off notes without task management needs.",
6489
- inputSchema: z28.object({
6490
- name: z28.string().min(1).max(160).describe("Name for the new Slack list.")
6545
+ inputSchema: z27.object({
6546
+ name: z27.string().min(1).max(160).describe("Name for the new Slack list.")
6491
6547
  }),
6492
6548
  outputSchema: juniorToolResultSchema,
6493
6549
  execute: async ({ name }) => {
@@ -6519,13 +6575,13 @@ function createSlackListCreateTool(state) {
6519
6575
  }
6520
6576
 
6521
6577
  // src/chat/slack/tools/list/get-items.ts
6522
- import { z as z29 } from "zod";
6578
+ import { z as z28 } from "zod";
6523
6579
  function createSlackListGetItemsTool(state) {
6524
6580
  return zodTool({
6525
6581
  description: "Read items from the active Slack list tracked in artifact context. Use when the user asks for task status, open items, or list contents. Do not use when list state is already known from the immediately prior result.",
6526
6582
  annotations: { readOnlyHint: true, destructiveHint: false },
6527
- inputSchema: z29.object({
6528
- limit: z29.coerce.number().int().min(1).max(200).describe("Maximum number of list items to return.").optional()
6583
+ inputSchema: z28.object({
6584
+ limit: z28.coerce.number().int().min(1).max(200).describe("Maximum number of list items to return.").optional()
6529
6585
  }),
6530
6586
  outputSchema: juniorToolResultSchema,
6531
6587
  execute: async ({ limit }) => {
@@ -6550,21 +6606,21 @@ function createSlackListGetItemsTool(state) {
6550
6606
  }
6551
6607
 
6552
6608
  // src/chat/slack/tools/list/update-item.ts
6553
- import { z as z30 } from "zod";
6554
- var booleanInput3 = (description) => z30.preprocess(
6609
+ import { z as z29 } from "zod";
6610
+ var booleanInput3 = (description) => z29.preprocess(
6555
6611
  (value) => value === "true" ? true : value === "false" ? false : value,
6556
- z30.boolean()
6612
+ z29.boolean()
6557
6613
  ).describe(description);
6558
- var updateListItemInputSchema = z30.union([
6559
- z30.object({
6560
- item_id: z30.string().min(1).describe("ID of the Slack list item to update."),
6614
+ var updateListItemInputSchema = z29.union([
6615
+ z29.object({
6616
+ item_id: z29.string().min(1).describe("ID of the Slack list item to update."),
6561
6617
  completed: booleanInput3("Optional completion status update."),
6562
- title: z30.string().min(1).describe("Optional new item title.").optional()
6618
+ title: z29.string().min(1).describe("Optional new item title.").optional()
6563
6619
  }),
6564
- z30.object({
6565
- item_id: z30.string().min(1).describe("ID of the Slack list item to update."),
6620
+ z29.object({
6621
+ item_id: z29.string().min(1).describe("ID of the Slack list item to update."),
6566
6622
  completed: booleanInput3("Optional completion status update.").optional(),
6567
- title: z30.string().min(1).describe("Optional new item title.")
6623
+ title: z29.string().min(1).describe("Optional new item title.")
6568
6624
  })
6569
6625
  ]);
6570
6626
  function createSlackListUpdateItemTool(state) {
@@ -6640,7 +6696,7 @@ async function checkSlackChannelReadAccess(args) {
6640
6696
  }
6641
6697
 
6642
6698
  // src/chat/slack/tools/thread-read.ts
6643
- import { z as z31 } from "zod";
6699
+ import { z as z30 } from "zod";
6644
6700
 
6645
6701
  // src/chat/slack/tools/slack-message-url.ts
6646
6702
  var SLACK_HOST_PATTERN = /^[a-z0-9-]+\.slack(?:-gov)?\.com$/;
@@ -6813,8 +6869,8 @@ function createSlackThreadReadTool(context, deps = {}) {
6813
6869
  return zodTool({
6814
6870
  description: "Read a Slack thread from a shared Slack message archive URL or explicit channel + timestamp. Use when the user shares a Slack message link (https://*.slack.com/archives/...) and you need the referenced message and its thread context. Only the current conversation and public channels Junior has seen in this workspace are readable.",
6815
6871
  annotations: { readOnlyHint: true, destructiveHint: false },
6816
- inputSchema: z31.object({
6817
- url: z31.string().min(1).describe(
6872
+ inputSchema: z30.object({
6873
+ url: z30.string().min(1).describe(
6818
6874
  "Slack message archive URL, e.g. https://workspace.slack.com/archives/C123/p1700000000123456"
6819
6875
  ).optional(),
6820
6876
  channel_id: slackChannelIdParam(
@@ -6823,8 +6879,8 @@ function createSlackThreadReadTool(context, deps = {}) {
6823
6879
  ts: slackTimestampParam(
6824
6880
  "Slack message timestamp (e.g. 1700000000.123456). May be the thread root or any message in the thread."
6825
6881
  ).optional(),
6826
- limit: z31.coerce.number().int().min(1).max(1e3).describe("Maximum number of thread messages to fetch.").optional(),
6827
- max_pages: z31.coerce.number().int().min(1).max(10).describe("Maximum number of Slack API pages to traverse.").optional()
6882
+ limit: z30.coerce.number().int().min(1).max(1e3).describe("Maximum number of thread messages to fetch.").optional(),
6883
+ max_pages: z30.coerce.number().int().min(1).max(10).describe("Maximum number of Slack API pages to traverse.").optional()
6828
6884
  }),
6829
6885
  outputSchema: juniorToolResultSchema,
6830
6886
  execute: async ({ url, channel_id, ts, limit, max_pages }) => {
@@ -7080,29 +7136,29 @@ async function searchSlackUsers(options) {
7080
7136
  }
7081
7137
 
7082
7138
  // src/chat/slack/tools/user-lookup.ts
7083
- import { z as z32 } from "zod";
7084
- var booleanInput4 = (description) => z32.preprocess(
7139
+ import { z as z31 } from "zod";
7140
+ var booleanInput4 = (description) => z31.preprocess(
7085
7141
  (value) => value === "true" ? true : value === "false" ? false : value,
7086
- z32.boolean()
7142
+ z31.boolean()
7087
7143
  ).describe(description);
7088
7144
  function createSlackUserLookupTool() {
7089
7145
  return zodTool({
7090
7146
  description: "Look up Slack user profiles by user ID, email, or name search. Use when you need to identify a user, resolve cross-platform identity, or look up profile details like title or status. Returns profile fields including custom fields. For user ID lookup, pass a Slack user ID (e.g. U039RR91S). For search, pass a name query.",
7091
7147
  annotations: { readOnlyHint: true, destructiveHint: false },
7092
- inputSchema: z32.object({
7148
+ inputSchema: z31.object({
7093
7149
  user_id: slackUserIdParam(
7094
7150
  "Slack user ID to look up (e.g. U039RR91S). Mutually exclusive with email and query."
7095
7151
  ).optional(),
7096
- email: z32.string().min(3).describe(
7152
+ email: z31.string().min(3).describe(
7097
7153
  "Email address to look up. Mutually exclusive with user_id and query."
7098
7154
  ).optional(),
7099
- query: z32.string().min(2).describe(
7155
+ query: z31.string().min(2).describe(
7100
7156
  "Name to search for (matches against username, display name, real name). Mutually exclusive with user_id and email."
7101
7157
  ).optional(),
7102
- limit: z32.coerce.number().int().min(1).max(20).describe(
7158
+ limit: z31.coerce.number().int().min(1).max(20).describe(
7103
7159
  "Maximum number of results to return for name search. Defaults to 10."
7104
7160
  ).optional(),
7105
- max_pages: z32.coerce.number().int().min(1).max(5).describe(
7161
+ max_pages: z31.coerce.number().int().min(1).max(5).describe(
7106
7162
  "Maximum number of Slack API pages to scan for name search. Defaults to 3."
7107
7163
  ).optional(),
7108
7164
  include_bots: booleanInput4(
@@ -7206,12 +7262,12 @@ function createSlackUserLookupTool() {
7206
7262
  }
7207
7263
 
7208
7264
  // src/chat/tools/system-time.ts
7209
- import { z as z33 } from "zod";
7265
+ import { z as z32 } from "zod";
7210
7266
  function createSystemTimeTool() {
7211
7267
  return zodTool({
7212
7268
  description: "Return current system time in UTC and local ISO formats. Use when the user asks for current time/date context. Do not use as a substitute for historical or timezone-conversion research.",
7213
7269
  annotations: { readOnlyHint: true, destructiveHint: false },
7214
- inputSchema: z33.object({}),
7270
+ inputSchema: z32.object({}),
7215
7271
  outputSchema: juniorToolResultSchema,
7216
7272
  execute: async () => {
7217
7273
  const now = /* @__PURE__ */ new Date();
@@ -7268,7 +7324,7 @@ function createStateAdvisorSessionStore() {
7268
7324
  }
7269
7325
 
7270
7326
  // src/chat/tools/advisor/tool.ts
7271
- import { z as z34 } from "zod";
7327
+ import { z as z33 } from "zod";
7272
7328
  var ADVISOR_TOOL_DESCRIPTION = "Second-opinion advisor for hard technical work. Call when you reach a consequential workflow moment: after enough exploration to frame a non-obvious plan; before a non-trivial code change or broad refactor; after repeated failures or when requirements or next steps feel ambiguous or risky; and when verifying a complex change before declaring it done, opening a PR, or reporting results. Ask a focused question and pass curated context: exact evidence, constraints, relevant code snippets, command output, diffs, current plan, and alternatives considered. The advisor has its own history, does not automatically receive the parent transcript, and may use read-only tools to verify. Follow up only with new evidence or changed constraints. Do not use for greetings, simple deterministic edits, routine formatting, or when fresh output makes the next action obvious.";
7273
7329
  var ADVISOR_SYSTEM_PROMPT = [
7274
7330
  "You are a senior technical advisor for the executor.",
@@ -7321,9 +7377,9 @@ function createAdvisorTool(context) {
7321
7377
  const spanContext = context.logContext ?? {};
7322
7378
  return zodTool({
7323
7379
  description: ADVISOR_TOOL_DESCRIPTION,
7324
- inputSchema: z34.object({
7325
- question: z34.string().min(1).describe("Focused advisor question or decision point."),
7326
- context: z34.string().min(1).describe(
7380
+ inputSchema: z33.object({
7381
+ question: z33.string().min(1).describe("Focused advisor question or decision point."),
7382
+ context: z33.string().min(1).describe(
7327
7383
  "Curated evidence packet: relevant requirements, constraints, current plan, alternatives, code snippets, diffs, command output, and open questions."
7328
7384
  )
7329
7385
  }),
@@ -7392,15 +7448,11 @@ function createAdvisorTool(context) {
7392
7448
  ].join("\n");
7393
7449
  const advisorInputMessage = {
7394
7450
  role: "user",
7395
- content: [
7396
- {
7397
- type: "text",
7398
- text: requestText
7399
- }
7400
- ]
7451
+ content: [{ type: "text", text: requestText }],
7452
+ timestamp: Date.now()
7401
7453
  };
7402
7454
  const advisorInputMessagesAttribute = serializeGenAiAttribute(
7403
- conversationPrivacy !== "public" ? [toGenAiMessageMetadata(advisorInputMessage)] : [advisorInputMessage]
7455
+ conversationPrivacy !== "public" ? [toGenAiMessageMetadata(advisorInputMessage)] : [toCanonicalInputMessage(advisorInputMessage)]
7404
7456
  );
7405
7457
  return await withSpan(
7406
7458
  `invoke_agent ${context.config.modelId}`,
@@ -7451,7 +7503,7 @@ function createAdvisorTool(context) {
7451
7503
  const newAdvisorMessages = advisorAgent.state.messages.slice(beforeMessageCount);
7452
7504
  const outputMessages = newAdvisorMessages.filter(isAssistantMessage);
7453
7505
  const outputMessagesAttribute = serializeGenAiAttribute(
7454
- conversationPrivacy !== "public" ? outputMessages.map(toGenAiMessageMetadata) : outputMessages
7506
+ conversationPrivacy !== "public" ? outputMessages.map(toGenAiMessageMetadata) : outputMessages.map(toCanonicalOutputMessage)
7455
7507
  );
7456
7508
  setSpanAttributes({
7457
7509
  ...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
@@ -7506,7 +7558,7 @@ function createAdvisorTool(context) {
7506
7558
  }
7507
7559
 
7508
7560
  // src/chat/tools/web/fetch-tool.ts
7509
- import { z as z35 } from "zod";
7561
+ import { z as z34 } from "zod";
7510
7562
 
7511
7563
  // src/chat/tools/web/constants.ts
7512
7564
  var USER_AGENT = "junior-bot/0.1";
@@ -7953,9 +8005,9 @@ function createWebFetchTool(hooks, options = {}) {
7953
8005
  destructiveHint: false,
7954
8006
  openWorldHint: true
7955
8007
  },
7956
- inputSchema: z35.object({
7957
- url: z35.string().min(1).describe("HTTP(S) URL to fetch."),
7958
- max_chars: z35.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
8008
+ inputSchema: z34.object({
8009
+ url: z34.string().min(1).describe("HTTP(S) URL to fetch."),
8010
+ max_chars: z34.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
7959
8011
  }),
7960
8012
  outputSchema: juniorToolResultSchema,
7961
8013
  execute: async ({ url, max_chars }) => {
@@ -8022,7 +8074,7 @@ function createWebFetchTool(hooks, options = {}) {
8022
8074
  }
8023
8075
 
8024
8076
  // src/chat/tools/web/search.ts
8025
- import { z as z36 } from "zod";
8077
+ import { z as z35 } from "zod";
8026
8078
  import { generateText } from "ai";
8027
8079
  import { createGatewayProvider } from "@ai-sdk/gateway";
8028
8080
  import { getModel } from "@earendil-works/pi-ai";
@@ -8073,9 +8125,9 @@ function createWebSearchTool(override) {
8073
8125
  destructiveHint: false,
8074
8126
  openWorldHint: true
8075
8127
  },
8076
- inputSchema: z36.object({
8077
- query: z36.string().min(1).max(500).describe("Search query."),
8078
- max_results: z36.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
8128
+ inputSchema: z35.object({
8129
+ query: z35.string().min(1).max(500).describe("Search query."),
8130
+ max_results: z35.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
8079
8131
  }),
8080
8132
  outputSchema: juniorToolResultSchema,
8081
8133
  execute: async ({ query, max_results }) => {
@@ -8145,14 +8197,14 @@ function createWebSearchTool(override) {
8145
8197
  }
8146
8198
 
8147
8199
  // src/chat/tools/sandbox/write-file.ts
8148
- import { z as z37 } from "zod";
8200
+ import { z as z36 } from "zod";
8149
8201
  function createWriteFileTool() {
8150
8202
  return zodTool({
8151
8203
  description: "Write UTF-8 content to a file in the sandbox workspace. Use for intentional file creation or deliberate full-file replacement after validation; use editFile instead for targeted changes to existing files. Do not use for exploratory analysis-only turns.",
8152
8204
  executionMode: "sequential",
8153
- inputSchema: z37.object({
8154
- path: z37.string().min(1).describe("Path to write in the sandbox workspace."),
8155
- content: z37.string().describe("UTF-8 file content to write.")
8205
+ inputSchema: z36.object({
8206
+ path: z36.string().min(1).describe("Path to write in the sandbox workspace."),
8207
+ content: z36.string().describe("UTF-8 file content to write.")
8156
8208
  }),
8157
8209
  outputSchema: juniorToolResultSchema,
8158
8210
  execute: async () => {
@@ -8546,7 +8598,7 @@ function upstreamPermissionDeniedText(value) {
8546
8598
  ...sso ? [`Provider SSO: ${sso}`] : [],
8547
8599
  ...command ? [`Command: ${command}`] : [],
8548
8600
  "",
8549
- "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.",
8601
+ "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.",
8550
8602
  ...stderr ? ["", `stderr:
8551
8603
  ${stderr}`] : [],
8552
8604
  ...stdout ? ["", `stdout:
@@ -8891,6 +8943,13 @@ import { randomBytes } from "crypto";
8891
8943
  import {
8892
8944
  sourceSchema as sourceSchema2
8893
8945
  } from "@sentry/junior-plugin-api";
8946
+
8947
+ // src/chat/oauth-authorization-message.ts
8948
+ function formatOAuthAuthorizationMessage(args) {
8949
+ return `${formatSlackLink(args.authorizationUrl, args.label)}. ${args.completionText}`;
8950
+ }
8951
+
8952
+ // src/chat/oauth-flow.ts
8894
8953
  var OAUTH_STATE_TTL_MS = 10 * 60 * 1e3;
8895
8954
  function optionalString(value) {
8896
8955
  return typeof value === "string" ? value : void 0;
@@ -9046,7 +9105,7 @@ async function startOAuthFlow(provider, input) {
9046
9105
  await getStateAdapter().set(
9047
9106
  `oauth-state:${state}`,
9048
9107
  {
9049
- userId: input.requesterId,
9108
+ userId: input.actorId,
9050
9109
  provider,
9051
9110
  ...input.channelId ? { channelId: input.channelId } : {},
9052
9111
  ...input.destination ? { destination: input.destination } : {},
@@ -9083,71 +9142,76 @@ async function startOAuthFlow(provider, input) {
9083
9142
  },
9084
9143
  "Initiated OAuth authorization code flow"
9085
9144
  );
9145
+ const authorizationUrl = `${providerConfig.authorizeEndpoint}?${authorizeParams.toString()}`;
9086
9146
  return {
9087
9147
  ok: true,
9088
9148
  delivery: await deliverPrivateMessage({
9089
9149
  channelId: input.channelId,
9090
9150
  threadTs: input.threadTs,
9091
- userId: input.requesterId,
9092
- text: `<${providerConfig.authorizeEndpoint}?${authorizeParams.toString()}|Click here to link your ${formatProviderLabel(provider)} account>. Once you've authorized, you'll see a confirmation in Slack.`
9151
+ userId: input.actorId,
9152
+ text: formatOAuthAuthorizationMessage({
9153
+ authorizationUrl,
9154
+ label: `Click here to link your ${formatProviderLabel(provider)} account`,
9155
+ completionText: "Once you've authorized, you'll see a confirmation in Slack."
9156
+ })
9093
9157
  })
9094
9158
  };
9095
9159
  }
9096
9160
 
9097
9161
  // src/chat/sandbox/egress/schemas.ts
9098
- import { z as z38 } from "zod";
9162
+ import { z as z37 } from "zod";
9099
9163
  import {
9100
9164
  pluginAuthorizationSchema,
9101
9165
  pluginCredentialHeaderTransformSchema,
9102
9166
  pluginGrantSchema,
9103
9167
  pluginProviderAccountSchema
9104
9168
  } from "@sentry/junior-plugin-api";
9105
- var finiteNumberSchema = z38.number().refine(Number.isFinite);
9106
- var httpStatusSchema = z38.number().int().min(100).max(599);
9107
- var providerNameSchema = z38.string().regex(/^[a-z][a-z0-9-]*$/);
9108
- var credentialSignalKindSchema = z38.enum(["auth_required", "unavailable"]);
9169
+ var finiteNumberSchema = z37.number().refine(Number.isFinite);
9170
+ var httpStatusSchema = z37.number().int().min(100).max(599);
9171
+ var providerNameSchema = z37.string().regex(/^[a-z][a-z0-9-]*$/);
9172
+ var credentialSignalKindSchema = z37.enum(["auth_required", "unavailable"]);
9109
9173
  var sandboxEgressGrantSchema = pluginGrantSchema;
9110
- var sandboxEgressCredentialContextSchema = z38.object({
9174
+ var sandboxEgressCredentialContextSchema = z37.object({
9111
9175
  credentials: credentialContextSchema,
9112
- egressId: z38.string().min(1),
9176
+ egressId: z37.string().min(1),
9113
9177
  expiresAtMs: finiteNumberSchema,
9114
- contextId: z38.string().min(1)
9178
+ contextId: z37.string().min(1)
9115
9179
  }).strict();
9116
- var sandboxEgressCredentialLeaseSchema = z38.object({
9180
+ var sandboxEgressCredentialLeaseSchema = z37.object({
9117
9181
  account: pluginProviderAccountSchema.optional(),
9118
9182
  authorization: pluginAuthorizationSchema.optional(),
9119
9183
  grant: sandboxEgressGrantSchema,
9120
9184
  provider: providerNameSchema,
9121
- expiresAt: z38.string().min(1),
9122
- headerTransforms: z38.array(pluginCredentialHeaderTransformSchema).min(1)
9185
+ expiresAt: z37.string().min(1),
9186
+ headerTransforms: z37.array(pluginCredentialHeaderTransformSchema).min(1)
9123
9187
  }).strict();
9124
- var sandboxEgressAuthRequiredSignalSchema = z38.object({
9188
+ var sandboxEgressAuthRequiredSignalSchema = z37.object({
9125
9189
  authorization: pluginAuthorizationSchema.optional(),
9126
9190
  grant: sandboxEgressGrantSchema,
9127
9191
  kind: credentialSignalKindSchema.default("auth_required"),
9128
9192
  provider: providerNameSchema,
9129
- message: z38.string().optional(),
9193
+ message: z37.string().optional(),
9130
9194
  createdAtMs: finiteNumberSchema
9131
9195
  }).strict().superRefine((signal, ctx) => {
9132
9196
  if (signal.authorization && signal.authorization.provider !== signal.provider) {
9133
9197
  ctx.addIssue({
9134
- code: z38.ZodIssueCode.custom,
9198
+ code: z37.ZodIssueCode.custom,
9135
9199
  message: "Auth signal authorization provider must match provider",
9136
9200
  path: ["authorization", "provider"]
9137
9201
  });
9138
9202
  }
9139
9203
  });
9140
- var sandboxEgressPermissionDeniedSignalSchema = z38.object({
9204
+ var sandboxEgressPermissionDeniedSignalSchema = z37.object({
9141
9205
  account: pluginProviderAccountSchema.optional(),
9142
- acceptedPermissions: z38.string().optional(),
9206
+ acceptedPermissions: z37.string().optional(),
9143
9207
  grant: sandboxEgressGrantSchema,
9144
- message: z38.string().min(1),
9208
+ message: z37.string().min(1),
9145
9209
  provider: providerNameSchema,
9146
- source: z38.literal("upstream"),
9147
- sso: z38.string().optional(),
9210
+ source: z37.literal("upstream"),
9211
+ sso: z37.string().optional(),
9148
9212
  status: httpStatusSchema,
9149
- upstreamHost: z38.string().min(1),
9150
- upstreamPath: z38.string().min(1),
9213
+ upstreamHost: z37.string().min(1),
9214
+ upstreamPath: z37.string().min(1),
9151
9215
  createdAtMs: finiteNumberSchema
9152
9216
  }).strict();
9153
9217
  function parseSandboxEgressAuthRequiredSignal(value) {
@@ -9199,7 +9263,7 @@ function createPluginAuthOrchestration(input) {
9199
9263
  if (pendingPause) {
9200
9264
  throw pendingPause;
9201
9265
  }
9202
- if (!input.requesterId || !pluginCatalogRuntime.getOAuthConfig(provider)) {
9266
+ if (!input.actorId || !pluginCatalogRuntime.getOAuthConfig(provider)) {
9203
9267
  throw new Error(`Cannot start plugin authorization for ${provider}`);
9204
9268
  }
9205
9269
  if (input.authorizationFlowMode === "disabled") {
@@ -9216,13 +9280,13 @@ function createPluginAuthOrchestration(input) {
9216
9280
  pendingAuth: input.pendingAuth,
9217
9281
  kind: "plugin",
9218
9282
  provider,
9219
- requesterId: input.requesterId,
9283
+ actorId: input.actorId,
9220
9284
  sessionId: input.sessionId,
9221
9285
  ...options?.scope ? { scope: options.scope } : {}
9222
9286
  }) : false;
9223
9287
  if (!reusingPendingLink) {
9224
9288
  const oauthResult = await startOAuthFlow(provider, {
9225
- requesterId: input.requesterId,
9289
+ actorId: input.actorId,
9226
9290
  channelId: input.channelId,
9227
9291
  destination: input.destination,
9228
9292
  source: input.source,
@@ -9242,14 +9306,14 @@ function createPluginAuthOrchestration(input) {
9242
9306
  );
9243
9307
  }
9244
9308
  }
9245
- if (options?.unlinkExistingProvider && input.requesterId && input.userTokenStore) {
9246
- await unlinkProvider(input.requesterId, provider, input.userTokenStore);
9309
+ if (options?.unlinkExistingProvider && input.actorId && input.userTokenStore) {
9310
+ await unlinkProvider(input.actorId, provider, input.userTokenStore);
9247
9311
  }
9248
9312
  if (input.sessionId && recordPendingAuth) {
9249
9313
  await recordPendingAuth({
9250
9314
  kind: "plugin",
9251
9315
  provider,
9252
- requesterId: input.requesterId,
9316
+ actorId: input.actorId,
9253
9317
  ...options?.scope ? { scope: options.scope } : {},
9254
9318
  sessionId: input.sessionId,
9255
9319
  linkSentAtMs: reusingPendingLink ? input.pendingAuth.linkSentAtMs : Date.now()
@@ -9260,7 +9324,7 @@ function createPluginAuthOrchestration(input) {
9260
9324
  conversationId: input.conversationId,
9261
9325
  kind: "plugin",
9262
9326
  provider,
9263
- requesterId: input.requesterId,
9327
+ actorId: input.actorId,
9264
9328
  authorizationId: authorizationId({
9265
9329
  kind: "plugin",
9266
9330
  provider,
@@ -9297,7 +9361,7 @@ function createPluginAuthOrchestration(input) {
9297
9361
  signal.message ?? `${formatProviderLabel(provider)} credentials are required but no OAuth flow is available for this provider.`
9298
9362
  );
9299
9363
  }
9300
- if (!input.requesterId || !input.userTokenStore) {
9364
+ if (!input.actorId || !input.userTokenStore) {
9301
9365
  if (input.authorizationFlowMode === "disabled") {
9302
9366
  throw new AuthorizationFlowDisabledError("plugin", provider);
9303
9367
  }
@@ -9404,15 +9468,15 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, trac
9404
9468
  }
9405
9469
 
9406
9470
  // src/chat/tools/execute-tool.ts
9407
- import { z as z39 } from "zod";
9471
+ import { z as z38 } from "zod";
9408
9472
  var EXECUTE_TOOL_NAME = "executeTool";
9409
9473
  function createExecuteToolTool() {
9410
9474
  return zodTool({
9411
9475
  description: "Execute any catalog tool by exact tool_name from searchTools. Put tool-specific parameters inside arguments.",
9412
9476
  executionMode: "sequential",
9413
- inputSchema: z39.object({
9414
- tool_name: z39.string().min(1).describe("Exact catalog tool_name returned by searchTools."),
9415
- arguments: z39.record(z39.string(), z39.unknown()).describe(
9477
+ inputSchema: z38.object({
9478
+ tool_name: z38.string().min(1).describe("Exact catalog tool_name returned by searchTools."),
9479
+ arguments: z38.record(z38.string(), z38.unknown()).describe(
9416
9480
  'Arguments matching the selected catalog tool schema, for example { "query": "..." }.'
9417
9481
  ).optional()
9418
9482
  }).strict(),
@@ -9452,13 +9516,54 @@ function planToolExposure(tools) {
9452
9516
  }
9453
9517
 
9454
9518
  // src/chat/tools/search-tools.ts
9455
- import { z as z40 } from "zod";
9519
+ import { z as z39 } from "zod";
9456
9520
  var SEARCH_TOOLS_NAME = "searchTools";
9457
9521
  var DEFAULT_MAX_RESULTS2 = 5;
9458
9522
  var MAX_RESULTS3 = 20;
9523
+ var MODEL_VISIBLE_DESCRIPTION_CAP = 180;
9524
+ var searchToolsSourceSchema = z39.object({
9525
+ id: z39.string(),
9526
+ description: z39.string()
9527
+ }).strict();
9528
+ var toolCallExampleSchema = z39.object({
9529
+ tool_name: z39.string(),
9530
+ arguments: z39.record(z39.string(), z39.string())
9531
+ }).strict();
9532
+ var searchToolsToolSchema = z39.object({
9533
+ tool_name: z39.string(),
9534
+ description: z39.string(),
9535
+ exposure: z39.enum(["direct", "deferred", "modelOnly", "hidden"]),
9536
+ source: z39.string().optional(),
9537
+ signature: z39.string(),
9538
+ call: toolCallExampleSchema,
9539
+ input_schema: z39.unknown(),
9540
+ input_schema_summary: z39.string(),
9541
+ call_notes: z39.array(z39.string()),
9542
+ annotations: z39.record(z39.string(), z39.unknown())
9543
+ }).strict();
9544
+ var searchToolsOutputSchema = juniorToolResultSchema.extend({
9545
+ query: z39.string().nullable(),
9546
+ source: z39.string().nullable(),
9547
+ sources: z39.array(searchToolsSourceSchema),
9548
+ total_catalog_tools: z39.number().int().nonnegative(),
9549
+ total_eligible_tools: z39.number().int().nonnegative(),
9550
+ total_matches: z39.number().int().nonnegative(),
9551
+ returned_tools: z39.number().int().nonnegative(),
9552
+ execution_tool: z39.literal("executeTool"),
9553
+ execution_example: toolCallExampleSchema,
9554
+ tools: z39.array(searchToolsToolSchema)
9555
+ }).strict();
9459
9556
  function normalize2(value) {
9460
9557
  return value.toLowerCase().replace(/[^a-z0-9_]+/g, " ").trim();
9461
9558
  }
9559
+ function summarizeModelVisibleDescription(description) {
9560
+ const paragraph = description.split(/\n\s*\n/).map((part) => part.trim()).find(Boolean) ?? "";
9561
+ const normalized = paragraph.replace(/\s+/g, " ").trim();
9562
+ if (normalized.length <= MODEL_VISIBLE_DESCRIPTION_CAP) {
9563
+ return normalized;
9564
+ }
9565
+ return `${normalized.slice(0, MODEL_VISIBLE_DESCRIPTION_CAP - 3).trimEnd()}...`;
9566
+ }
9462
9567
  function schemaText(schema) {
9463
9568
  try {
9464
9569
  return JSON.stringify(schema);
@@ -9473,6 +9578,8 @@ function searchableToolText2(name, definition) {
9473
9578
  definition.identity?.id,
9474
9579
  definition.identity?.name,
9475
9580
  definition.identity?.plugin,
9581
+ definition.source?.id,
9582
+ definition.source?.description,
9476
9583
  definition.description,
9477
9584
  definition.promptSnippet,
9478
9585
  ...definition.promptGuidelines ?? [],
@@ -9481,15 +9588,16 @@ function searchableToolText2(name, definition) {
9481
9588
  ].filter(Boolean).join(" ")
9482
9589
  );
9483
9590
  }
9484
- function searchCatalogTools(tools, query) {
9591
+ function searchCatalogTools(tools, query, source) {
9485
9592
  const entries = Object.entries(tools).sort(
9486
9593
  ([left], [right]) => left.localeCompare(right)
9487
9594
  );
9595
+ const sourceEntries = source ? entries.filter(([, definition]) => definition.source?.id === source) : entries;
9488
9596
  if (!normalize2(query)) {
9489
- return entries.map(([name]) => name);
9597
+ return sourceEntries.map(([name]) => name);
9490
9598
  }
9491
9599
  const terms = normalize2(query).split(/\s+/).filter(Boolean);
9492
- return entries.filter(([name, definition]) => {
9600
+ return sourceEntries.filter(([name, definition]) => {
9493
9601
  const text = searchableToolText2(name, definition);
9494
9602
  return terms.every((term) => text.includes(term));
9495
9603
  }).map(([name]) => name);
@@ -9500,17 +9608,128 @@ function callNotes(definition) {
9500
9608
  ...definition.promptGuidelines?.map((guideline) => guideline.trim()).filter(Boolean) ?? []
9501
9609
  ];
9502
9610
  }
9503
- function toolMetadata(name, definition) {
9611
+ function getSchemaProperties3(schema) {
9612
+ if (!schema || typeof schema !== "object" || !("properties" in schema)) {
9613
+ return {};
9614
+ }
9615
+ const properties = schema.properties;
9616
+ return properties && typeof properties === "object" && !Array.isArray(properties) ? properties : {};
9617
+ }
9618
+ function getRequiredFields3(schema) {
9619
+ if (!schema || typeof schema !== "object" || !("required" in schema)) {
9620
+ return /* @__PURE__ */ new Set();
9621
+ }
9622
+ const required = schema.required;
9623
+ return Array.isArray(required) ? new Set(
9624
+ required.filter((value) => typeof value === "string")
9625
+ ) : /* @__PURE__ */ new Set();
9626
+ }
9627
+ function formatSchemaType2(schema) {
9628
+ if (!schema || typeof schema !== "object") {
9629
+ return "unknown";
9630
+ }
9631
+ const typed = schema;
9632
+ const type = typed.type;
9633
+ if (typeof type === "string") {
9634
+ if (type === "array") {
9635
+ return `${formatSchemaType2(typed.items)}[]`;
9636
+ }
9637
+ return type;
9638
+ }
9639
+ if (Array.isArray(type)) {
9640
+ return type.filter((value) => typeof value === "string").join(" | ");
9641
+ }
9642
+ if (Array.isArray(typed.enum) && typed.enum.length > 0) {
9643
+ return typed.enum.map((value) => JSON.stringify(value)).join(" | ");
9644
+ }
9645
+ return "unknown";
9646
+ }
9647
+ function formatArgumentPlaceholder2(name, schema) {
9648
+ const type = formatSchemaType2(schema);
9649
+ if (type === "string") {
9650
+ return `<${name}>`;
9651
+ }
9652
+ if (type === "number" || type === "integer") {
9653
+ return "<number>";
9654
+ }
9655
+ if (type === "boolean") {
9656
+ return "<boolean>";
9657
+ }
9658
+ if (type.endsWith("[]")) {
9659
+ return "<array>";
9660
+ }
9661
+ if (type === "object") {
9662
+ return "<object>";
9663
+ }
9664
+ return `<${type}>`;
9665
+ }
9666
+ function formatToolSignature(name, schema) {
9667
+ const properties = getSchemaProperties3(schema);
9668
+ const required = getRequiredFields3(schema);
9669
+ const fields = Object.entries(properties).map(([field, propertySchema]) => {
9670
+ const marker = required.has(field) ? "" : "?";
9671
+ return `${field}${marker}: ${formatSchemaType2(propertySchema)}`;
9672
+ });
9673
+ return fields.length > 0 ? `${name}({ ${fields.join(", ")} })` : `${name}()`;
9674
+ }
9675
+ function formatToolCallExample(name, schema) {
9504
9676
  return {
9505
9677
  tool_name: name,
9506
- description: definition.description,
9678
+ arguments: Object.fromEntries(
9679
+ Object.entries(getSchemaProperties3(schema)).map(
9680
+ ([field, propertySchema]) => [
9681
+ field,
9682
+ formatArgumentPlaceholder2(field, propertySchema)
9683
+ ]
9684
+ )
9685
+ )
9686
+ };
9687
+ }
9688
+ function sourceSummaries(tools) {
9689
+ const sources = /* @__PURE__ */ new Map();
9690
+ for (const definition of Object.values(tools)) {
9691
+ if (!definition.source) {
9692
+ continue;
9693
+ }
9694
+ sources.set(definition.source.id, {
9695
+ id: definition.source.id,
9696
+ description: summarizeModelVisibleDescription(
9697
+ definition.source.description
9698
+ )
9699
+ });
9700
+ }
9701
+ return [...sources.values()].sort(
9702
+ (left, right) => left.id.localeCompare(right.id)
9703
+ );
9704
+ }
9705
+ function selectedSourceSummaries(tools, matches, requestedSource, knownSources) {
9706
+ if (requestedSource) {
9707
+ return knownSources.filter((source) => source.id === requestedSource);
9708
+ }
9709
+ const matchedSourceIds = new Set(
9710
+ matches.map((name) => tools[name]?.source?.id).filter((source) => Boolean(source))
9711
+ );
9712
+ return knownSources.filter((source) => matchedSourceIds.has(source.id));
9713
+ }
9714
+ function renderSearchToolsDescription(knownSources) {
9715
+ 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.";
9716
+ if (knownSources.length === 0) {
9717
+ return intro;
9718
+ }
9719
+ return [
9720
+ intro,
9721
+ "Available sources:",
9722
+ ...knownSources.map((source) => `- ${source.id}: ${source.description}`)
9723
+ ].join("\n");
9724
+ }
9725
+ function toolMetadata(name, definition, includeSource) {
9726
+ return {
9727
+ tool_name: name,
9728
+ description: summarizeModelVisibleDescription(definition.description),
9507
9729
  exposure: effectiveToolExposure(definition),
9508
- source: definition.identity ? {
9509
- type: "plugin",
9510
- id: definition.identity.id,
9511
- name: definition.identity.name,
9512
- plugin: definition.identity.plugin
9513
- } : { type: "core" },
9730
+ ...includeSource && definition.source ? { source: definition.source.id } : {},
9731
+ signature: formatToolSignature(name, definition.inputSchema),
9732
+ call: formatToolCallExample(name, definition.inputSchema),
9514
9733
  input_schema: definition.inputSchema,
9515
9734
  input_schema_summary: summarizeInputSchema(
9516
9735
  definition.inputSchema
@@ -9520,27 +9739,55 @@ function toolMetadata(name, definition) {
9520
9739
  };
9521
9740
  }
9522
9741
  function createSearchToolsTool(catalogTools) {
9742
+ const knownSources = sourceSummaries(catalogTools);
9523
9743
  return zodTool({
9524
- description: "Search the executable tool catalog. Use this to discover exact tool names, owners, schemas, and call notes before calling executeTool.",
9744
+ description: renderSearchToolsDescription(knownSources),
9525
9745
  annotations: { readOnlyHint: true, destructiveHint: false },
9526
- inputSchema: z40.object({
9527
- query: z40.string().nullable().describe(
9746
+ inputSchema: z39.object({
9747
+ query: z39.string().nullable().describe(
9528
9748
  "Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
9529
9749
  ).optional(),
9530
- max_results: z40.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
9750
+ source: z39.string().nullable().describe(
9751
+ "Optional source id to search within, such as a plugin source returned in sources."
9752
+ ).optional(),
9753
+ max_results: z39.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
9531
9754
  }).strict(),
9532
- outputSchema: juniorToolResultSchema,
9533
- execute: async ({ query, max_results }) => {
9755
+ outputSchema: searchToolsOutputSchema,
9756
+ execute: async ({ query, source, max_results }) => {
9534
9757
  const maxResults = max_results ?? DEFAULT_MAX_RESULTS2;
9535
- const matches = searchCatalogTools(catalogTools, query ?? "").slice(
9536
- 0,
9537
- maxResults
9758
+ const requestedSource = source ?? null;
9759
+ const sourceExists = requestedSource === null || knownSources.some((candidate) => candidate.id === requestedSource);
9760
+ const allMatches = sourceExists ? searchCatalogTools(catalogTools, query ?? "", requestedSource) : [];
9761
+ const matches = allMatches.slice(0, maxResults);
9762
+ const sources = !sourceExists ? knownSources : (query ?? "").trim() ? selectedSourceSummaries(
9763
+ catalogTools,
9764
+ matches,
9765
+ requestedSource,
9766
+ knownSources
9767
+ ) : requestedSource ? knownSources.filter(
9768
+ (candidate) => candidate.id === requestedSource
9769
+ ) : knownSources;
9770
+ const totalEligibleTools = sourceExists ? searchCatalogTools(catalogTools, "", requestedSource).length : 0;
9771
+ const includePerToolSource = requestedSource === null;
9772
+ const renderedTools = matches.map(
9773
+ (name) => toolMetadata(name, catalogTools[name], includePerToolSource)
9538
9774
  );
9539
9775
  const data = {
9540
9776
  query: query ?? null,
9777
+ source: requestedSource,
9778
+ sources,
9541
9779
  total_catalog_tools: Object.keys(catalogTools).length,
9542
- returned_tools: matches.length,
9543
- tools: matches.map((name) => toolMetadata(name, catalogTools[name]))
9780
+ total_eligible_tools: totalEligibleTools,
9781
+ total_matches: allMatches.length,
9782
+ returned_tools: renderedTools.length,
9783
+ execution_tool: "executeTool",
9784
+ execution_example: {
9785
+ tool_name: "<returned tool_name>",
9786
+ arguments: {
9787
+ "<argument>": "<value from input_schema>"
9788
+ }
9789
+ },
9790
+ tools: renderedTools
9544
9791
  };
9545
9792
  return {
9546
9793
  ok: true,
@@ -9752,7 +9999,7 @@ var SANDBOX_EGRESS_LEASE_PREFIX = "sandbox-egress-lease";
9752
9999
  var DEFAULT_SESSION_TTL_MS = 30 * 60 * 1e3;
9753
10000
  function leaseKey(provider, grantName, context) {
9754
10001
  const actor = context.credentials.actor;
9755
- const actorKey = actor.type === "user" ? `user:${actor.userId}` : `system:${actor.id}`;
10002
+ const actorKey = "type" in actor ? `user:${actor.userId}` : `system:${actor.name}`;
9756
10003
  return `${SANDBOX_EGRESS_LEASE_PREFIX}:${provider}:${grantName}:${actorKey}:${context.egressId}:${context.contextId}`;
9757
10004
  }
9758
10005
  function authSignalKey(egressId, access) {
@@ -11594,6 +11841,13 @@ function createClientMetadata(callbackUrl) {
11594
11841
  token_endpoint_auth_method: "none"
11595
11842
  };
11596
11843
  }
11844
+ function clientAllowsRedirectUri(clientInformation, callbackUrl) {
11845
+ const redirectUris = clientInformation.redirect_uris;
11846
+ if (!Array.isArray(redirectUris)) {
11847
+ return true;
11848
+ }
11849
+ return redirectUris.includes(callbackUrl);
11850
+ }
11597
11851
  var StateBackedMcpOAuthClientProvider = class {
11598
11852
  constructor(authSessionId, callbackUrl, sessionContext) {
11599
11853
  this.authSessionId = authSessionId;
@@ -11617,7 +11871,14 @@ var StateBackedMcpOAuthClientProvider = class {
11617
11871
  session.userId,
11618
11872
  session.provider
11619
11873
  );
11620
- return credentials?.clientInformation;
11874
+ const clientInformation = credentials?.clientInformation;
11875
+ if (!clientInformation) {
11876
+ return void 0;
11877
+ }
11878
+ if (clientAllowsRedirectUri(clientInformation, this.callbackUrl)) {
11879
+ return clientInformation;
11880
+ }
11881
+ return void 0;
11621
11882
  }
11622
11883
  async saveClientInformation(clientInformation) {
11623
11884
  const session = await this.getCredentialContext();
@@ -11857,7 +12118,7 @@ function createMcpAuthOrchestration(input) {
11857
12118
  let pendingPause;
11858
12119
  const authSessionIdsByProvider = /* @__PURE__ */ new Map();
11859
12120
  const authProviderFactory = async (plugin) => {
11860
- if (!input.conversationId || !input.sessionId || !input.requesterId) {
12121
+ if (!input.conversationId || !input.sessionId || !input.actorId) {
11861
12122
  return void 0;
11862
12123
  }
11863
12124
  if (!input.recordPendingAuth && input.authorizationFlowMode !== "disabled") {
@@ -11871,7 +12132,7 @@ function createMcpAuthOrchestration(input) {
11871
12132
  destination: input.destination,
11872
12133
  source: input.source,
11873
12134
  sessionId: input.sessionId,
11874
- userId: input.requesterId,
12135
+ userId: input.actorId,
11875
12136
  userMessage: input.userMessage,
11876
12137
  ...input.channelId ? { channelId: input.channelId } : {},
11877
12138
  ...input.threadTs ? { threadTs: input.threadTs } : {},
@@ -11889,8 +12150,8 @@ function createMcpAuthOrchestration(input) {
11889
12150
  const authSessionId = authSessionIdsByProvider.get(provider);
11890
12151
  const conversationId = input.conversationId;
11891
12152
  const sessionId = input.sessionId;
11892
- const requesterId = input.requesterId;
11893
- if (!authSessionId || !conversationId || !sessionId || !requesterId) {
12153
+ const actorId = input.actorId;
12154
+ if (!authSessionId || !conversationId || !sessionId || !actorId) {
11894
12155
  throw new Error(
11895
12156
  `Missing MCP auth session context for plugin "${provider}"`
11896
12157
  );
@@ -11919,7 +12180,7 @@ function createMcpAuthOrchestration(input) {
11919
12180
  pendingAuth: input.pendingAuth,
11920
12181
  kind: "mcp",
11921
12182
  provider,
11922
- requesterId,
12183
+ actorId,
11923
12184
  sessionId
11924
12185
  });
11925
12186
  const providerLabel = formatProviderLabel(provider);
@@ -11928,7 +12189,11 @@ function createMcpAuthOrchestration(input) {
11928
12189
  channelId: authSession.channelId,
11929
12190
  threadTs: authSession.threadTs,
11930
12191
  userId: authSession.userId,
11931
- text: `<${authSession.authorizationUrl}|Click here to link your ${providerLabel} MCP access>. Once you've authorized, this thread will continue automatically.`
12192
+ text: formatOAuthAuthorizationMessage({
12193
+ authorizationUrl: authSession.authorizationUrl,
12194
+ label: `Click here to link your ${providerLabel} MCP access`,
12195
+ completionText: "Once you've authorized, this thread will continue automatically."
12196
+ })
11932
12197
  });
11933
12198
  if (!delivery) {
11934
12199
  throw new Error(
@@ -11941,7 +12206,7 @@ function createMcpAuthOrchestration(input) {
11941
12206
  await recordPendingAuth({
11942
12207
  kind: "mcp",
11943
12208
  provider,
11944
- requesterId,
12209
+ actorId,
11945
12210
  sessionId,
11946
12211
  linkSentAtMs: reusingPendingLink ? input.pendingAuth.linkSentAtMs : Date.now()
11947
12212
  });
@@ -11949,7 +12214,7 @@ function createMcpAuthOrchestration(input) {
11949
12214
  conversationId,
11950
12215
  kind: "mcp",
11951
12216
  provider,
11952
- requesterId,
12217
+ actorId,
11953
12218
  authorizationId: authorizationId2({
11954
12219
  kind: "mcp",
11955
12220
  provider,
@@ -12111,7 +12376,7 @@ async function issuePluginCredential(input) {
12111
12376
  if (!plugin || !hook) {
12112
12377
  throw new Error(`Plugin "${input.provider}" has no issueCredential hook`);
12113
12378
  }
12114
- const currentUserId = input.actor.type === "user" ? input.actor.userId : void 0;
12379
+ const currentUserId = "type" in input.actor ? input.actor.userId : void 0;
12115
12380
  const credentialSubjectUserId = input.credentialSubject?.userId;
12116
12381
  const result = await hook({
12117
12382
  ...basePluginContext(plugin),
@@ -13090,10 +13355,10 @@ async function writeSandboxGeneratedArtifacts(sandbox, files) {
13090
13355
  // src/chat/agent/tools.ts
13091
13356
  async function wireAgentTools(args) {
13092
13357
  const runSource = args.routing.source;
13093
- const authRequesterId = args.routing.credentialContext?.actor.type === "user" ? args.routing.credentialContext.actor.userId : void 0;
13358
+ const authActorId = args.routing.credentialContext && "type" in args.routing.credentialContext.actor ? args.routing.credentialContext.actor.userId : void 0;
13094
13359
  const userTokenStore = createUserTokenStore();
13095
13360
  const pluginHooks = createPluginHookRunner({
13096
- requester: args.actorRequester
13361
+ actor: args.currentActor
13097
13362
  });
13098
13363
  const sandboxExecutor = createSandboxExecutor({
13099
13364
  sandboxId: args.state.sandbox?.sandboxId,
@@ -13113,7 +13378,7 @@ async function wireAgentTools(args) {
13113
13378
  const result = await maybeExecuteJrRpcCustomCommand(command, {
13114
13379
  activeSkill: args.skillSandbox.getActiveSkill(),
13115
13380
  channelConfiguration: args.policy.channelConfiguration,
13116
- requesterId: args.actorRequester?.userId,
13381
+ actorId: isUserActor(args.currentActor) ? args.currentActor.userId : void 0,
13117
13382
  onConfigurationValueChanged: (key, value) => {
13118
13383
  if (value === void 0) {
13119
13384
  delete args.configurationValues[key];
@@ -13137,7 +13402,7 @@ async function wireAgentTools(args) {
13137
13402
  abortAgent: args.abortAgent,
13138
13403
  conversationId: args.sessionConversationId,
13139
13404
  sessionId: args.sessionId,
13140
- requesterId: authRequesterId,
13405
+ actorId: authActorId,
13141
13406
  channelId: slackChannelId,
13142
13407
  destination: args.routing.destination,
13143
13408
  source: runSource,
@@ -13158,7 +13423,7 @@ async function wireAgentTools(args) {
13158
13423
  abortAgent: args.abortAgent,
13159
13424
  conversationId: args.sessionConversationId,
13160
13425
  sessionId: args.sessionId,
13161
- requesterId: authRequesterId,
13426
+ actorId: authActorId,
13162
13427
  channelId: slackChannelId,
13163
13428
  destination: args.routing.destination,
13164
13429
  source: runSource,
@@ -13228,7 +13493,7 @@ async function wireAgentTools(args) {
13228
13493
  toolRuntimeContext = {
13229
13494
  ...commonToolRuntimeContext,
13230
13495
  destination: toolDestination,
13231
- requester: args.actorRequester?.platform === "slack" ? args.actorRequester : void 0,
13496
+ actor: args.currentActor?.platform === "slack" ? args.currentActor : void 0,
13232
13497
  source: runSource
13233
13498
  };
13234
13499
  } else {
@@ -13238,7 +13503,7 @@ async function wireAgentTools(args) {
13238
13503
  toolRuntimeContext = {
13239
13504
  ...commonToolRuntimeContext,
13240
13505
  destination: toolDestination,
13241
- requester: args.actorRequester?.platform === "local" ? args.actorRequester : void 0,
13506
+ actor: args.currentActor?.platform === "local" ? args.currentActor : void 0,
13242
13507
  source: runSource
13243
13508
  };
13244
13509
  }
@@ -13413,7 +13678,7 @@ function createResumeState(args) {
13413
13678
  sessionId: args.sessionId,
13414
13679
  loadedSkillNames: args.getLoadedSkillNames(),
13415
13680
  logContext: args.logContext,
13416
- requester: args.requester,
13681
+ actor: args.actor,
13417
13682
  ...args.surface ? { surface: args.surface } : {}
13418
13683
  });
13419
13684
  return {
@@ -13451,7 +13716,7 @@ function createResumeState(args) {
13451
13716
  await args.durability.onInputCommitted?.();
13452
13717
  inputCommitted = true;
13453
13718
  },
13454
- async persistSafeBoundary(messages) {
13719
+ async persistSafeBoundary(messages, trailingMessageProvenance) {
13455
13720
  if (!canPersistSession) {
13456
13721
  return false;
13457
13722
  }
@@ -13459,6 +13724,7 @@ function createResumeState(args) {
13459
13724
  ...sessionRecordBase(),
13460
13725
  sliceId: currentSliceId,
13461
13726
  messages,
13727
+ ...trailingMessageProvenance ? { trailingMessageProvenance } : {},
13462
13728
  ...turnStartMessageIndex !== void 0 ? { turnStartMessageIndex } : {}
13463
13729
  });
13464
13730
  if (!persisted) {
@@ -13467,8 +13733,11 @@ function createResumeState(args) {
13467
13733
  latestSafeBoundaryMessages = [...messages];
13468
13734
  return true;
13469
13735
  },
13470
- async requireDurableInputCheckpoint(messages) {
13471
- const persisted = await this.persistSafeBoundary(messages);
13736
+ async requireDurableInputCheckpoint(messages, trailingMessageProvenance) {
13737
+ const persisted = await this.persistSafeBoundary(
13738
+ messages,
13739
+ trailingMessageProvenance
13740
+ );
13472
13741
  if (!persisted && args.durability.onInputCommitted) {
13473
13742
  throw new TurnInputCommitLostError(
13474
13743
  `Durable turn input could not be checkpointed for conversation=${args.sessionConversationId ?? "unknown"} session=${args.sessionId ?? "unknown"}`
@@ -13627,7 +13896,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13627
13896
  if (!routing.destination) {
13628
13897
  throw new TypeError("Assistant reply generation requires a destination");
13629
13898
  }
13630
- assertRequesterDestinationMatch(routing);
13899
+ assertActorDestinationMatch(routing);
13631
13900
  assertCorrelationDestinationMatch(routing);
13632
13901
  const replyStartedAtMs = Date.now();
13633
13902
  const configuredTurnDeadlineAtMs = replyStartedAtMs + botConfig.turnTimeoutMs;
@@ -13642,18 +13911,18 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13642
13911
  let canRecordMcpProviders = false;
13643
13912
  let turnUsage;
13644
13913
  let thinkingSelection;
13645
- const requester = requesterFromRouting(routing);
13914
+ const actor = actorFromRouting(routing);
13646
13915
  const surface = surfaceFromRouting(routing);
13647
13916
  const runSource = routing.source;
13648
13917
  const userInput = input.messageText;
13649
13918
  const credentialActor = routing.credentialContext?.actor;
13650
13919
  const credentialActorLogContext = credentialActor ? {
13651
- actorType: credentialActor.type,
13652
- actorId: credentialActor.type === "user" ? credentialActor.userId : credentialActor.id
13920
+ actorType: "type" in credentialActor ? credentialActor.type : "system",
13921
+ actorId: "type" in credentialActor ? credentialActor.userId : credentialActor.name
13653
13922
  } : {};
13654
13923
  const sessionRecordLogContext = {
13655
13924
  threadId: routing.correlation?.threadId,
13656
- requesterId: routing.correlation?.requesterId,
13925
+ actorId: routing.correlation?.actorId,
13657
13926
  channelId: routing.correlation?.channelId,
13658
13927
  runId: routing.correlation?.runId,
13659
13928
  ...credentialActorLogContext,
@@ -13689,7 +13958,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13689
13958
  const spanContext = {
13690
13959
  conversationId: sessionConversationId,
13691
13960
  slackThreadId: routing.correlation?.threadId,
13692
- slackUserId: routing.correlation?.requesterId,
13961
+ slackUserId: routing.correlation?.actorId,
13693
13962
  slackChannelId: routing.correlation?.channelId,
13694
13963
  runId: routing.correlation?.runId,
13695
13964
  ...credentialActorLogContext,
@@ -13743,7 +14012,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13743
14012
  getLoadedSkillNames: () => loadedSkillNamesForResume,
13744
14013
  logContext: sessionRecordLogContext,
13745
14014
  recordActiveMcpProviders,
13746
- requester,
14015
+ actor,
13747
14016
  runSource,
13748
14017
  sessionConversationId,
13749
14018
  sessionId,
@@ -13804,7 +14073,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13804
14073
  context: {
13805
14074
  threadId: routing.correlation?.threadId,
13806
14075
  channelId: routing.correlation?.channelId,
13807
- requesterId: routing.correlation?.requesterId,
14076
+ actorId: routing.correlation?.actorId,
13808
14077
  runId: routing.correlation?.runId
13809
14078
  },
13810
14079
  currentTurnBlocks: routerBlocks,
@@ -13813,7 +14082,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13813
14082
  });
13814
14083
  setSpanAttributes({
13815
14084
  "gen_ai.request.model": botConfig.modelId,
13816
- "app.ai.reasoning_effort": thinkingSelection.thinkingLevel,
14085
+ "gen_ai.request.reasoning.level": thinkingSelection.thinkingLevel,
13817
14086
  "app.ai.thinking_level_reason": thinkingSelection.reason,
13818
14087
  ...thinkingSelection.confidence !== void 0 ? {
13819
14088
  "app.ai.thinking_level_confidence": thinkingSelection.confidence
@@ -13827,7 +14096,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13827
14096
  setTags({
13828
14097
  conversationId: spanContext.conversationId,
13829
14098
  slackThreadId: routing.correlation?.threadId,
13830
- slackUserId: routing.correlation?.requesterId,
14099
+ slackUserId: routing.correlation?.actorId,
13831
14100
  slackChannelId: routing.correlation?.channelId,
13832
14101
  runId: routing.correlation?.runId,
13833
14102
  ...credentialActorLogContext,
@@ -13837,7 +14106,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13837
14106
  const wiring = await wireAgentTools({
13838
14107
  abortAgent: () => agent?.abort(),
13839
14108
  activeSkills,
13840
- actorRequester: requester,
14109
+ currentActor: actor,
13841
14110
  artifactStatePatch,
13842
14111
  availableSkills,
13843
14112
  configurationValues,
@@ -13879,7 +14148,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13879
14148
  shouldPromptAgent
13880
14149
  } = await assemblePrompt({
13881
14150
  activeMcpCatalogs: wiring.activeMcpCatalogs,
13882
- actorRequester: requester,
14151
+ currentActor: actor,
13883
14152
  artifactState: state.artifactState,
13884
14153
  availableSkills,
13885
14154
  configurationValues,
@@ -13908,10 +14177,10 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13908
14177
  if (piMessages.length === 0) {
13909
14178
  return;
13910
14179
  }
13911
- await runResume.requireDurableInputCheckpoint([
13912
- ...agent.state.messages,
13913
- ...piMessages
13914
- ]);
14180
+ await runResume.requireDurableInputCheckpoint(
14181
+ [...agent.state.messages, ...piMessages],
14182
+ messages.map((message) => message.provenance)
14183
+ );
13915
14184
  for (const message of piMessages) {
13916
14185
  agent.steer(message);
13917
14186
  }
@@ -14066,7 +14335,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
14066
14335
  "gen_ai.operation.name": "invoke_agent",
14067
14336
  "gen_ai.request.model": botConfig.modelId,
14068
14337
  ...thinkingSelection ? {
14069
- "app.ai.reasoning_effort": thinkingSelection.thinkingLevel
14338
+ "gen_ai.request.reasoning.level": thinkingSelection.thinkingLevel
14070
14339
  } : {},
14071
14340
  "app.ai.turn_timeout_ms": turnTimeoutBudgetMs,
14072
14341
  "app.ai.turn_deadline_remaining_ms": Math.max(
@@ -14119,7 +14388,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
14119
14388
  );
14120
14389
  const outputMessages = newMessages.filter(isAssistantMessage);
14121
14390
  const outputMessagesAttribute = serializeGenAiAttribute(
14122
- conversationPrivacy !== "public" ? outputMessages.map(toGenAiMessageMetadata) : outputMessages
14391
+ conversationPrivacy !== "public" ? outputMessages.map(toGenAiMessageMetadata) : outputMessages.map(toCanonicalOutputMessage)
14123
14392
  );
14124
14393
  const usageSummary = extractGenAiUsageSummary(
14125
14394
  promptResult,
@@ -14175,7 +14444,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
14175
14444
  ...sessionConversationId ? { "app.ai.session.conversation_id": sessionConversationId } : {},
14176
14445
  ...sessionId ? { "app.ai.turn.session_id": sessionId } : {},
14177
14446
  ...currentSliceId ? { "app.ai.turn.slice_id": currentSliceId } : {},
14178
- "app.ai.reasoning_effort": thinkingSelection.thinkingLevel,
14447
+ "gen_ai.request.reasoning.level": thinkingSelection.thinkingLevel,
14179
14448
  ...toGenAiMessagesTraceAttributes("app.ai.input", inputMessages),
14180
14449
  ...inputMessagesAttribute ? { "gen_ai.input.messages": inputMessagesAttribute } : {}
14181
14450
  }
@@ -14233,7 +14502,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
14233
14502
  "assistant_reply_generation_failed",
14234
14503
  {
14235
14504
  slackThreadId: routing.correlation?.threadId,
14236
- slackUserId: routing.correlation?.requesterId,
14505
+ slackUserId: routing.correlation?.actorId,
14237
14506
  slackChannelId: routing.correlation?.channelId,
14238
14507
  runId: routing.correlation?.runId,
14239
14508
  ...credentialActorLogContext,