@sentry/junior 0.74.1 → 0.76.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 (121) hide show
  1. package/README.md +1 -1
  2. package/bin/junior.mjs +4 -66
  3. package/dist/agent-hooks-ZOE7RIED.js +37 -0
  4. package/dist/api-reference.d.ts +3 -1
  5. package/dist/app.js +5516 -5422
  6. package/dist/build/copy-build-content.d.ts +1 -1
  7. package/dist/build/virtual-config.d.ts +2 -2
  8. package/dist/chat/agent-dispatch/context.d.ts +2 -3
  9. package/dist/chat/agent-dispatch/runner.d.ts +2 -0
  10. package/dist/chat/agent-dispatch/types.d.ts +2 -1
  11. package/dist/chat/config.d.ts +3 -0
  12. package/dist/chat/credentials/state-adapter-token-store.d.ts +2 -0
  13. package/dist/chat/credentials/subject.d.ts +3 -3
  14. package/dist/chat/credentials/user-token-store.d.ts +17 -12
  15. package/dist/chat/db.d.ts +8 -0
  16. package/dist/chat/mcp/auth-store.d.ts +2 -1
  17. package/dist/chat/mcp/oauth.d.ts +2 -1
  18. package/dist/chat/oauth-flow.d.ts +3 -1
  19. package/dist/chat/pi/client.d.ts +15 -7
  20. package/dist/chat/plugins/agent-hooks.d.ts +20 -13
  21. package/dist/chat/plugins/auth/oauth-request.d.ts +11 -7
  22. package/dist/chat/plugins/credential-hooks.d.ts +6 -6
  23. package/dist/chat/plugins/logging.d.ts +2 -2
  24. package/dist/chat/plugins/model.d.ts +9 -0
  25. package/dist/chat/plugins/package-discovery.d.ts +2 -1
  26. package/dist/chat/plugins/prompt.d.ts +5 -0
  27. package/dist/chat/plugins/registry.d.ts +4 -0
  28. package/dist/chat/plugins/state.d.ts +3 -5
  29. package/dist/chat/plugins/task-callback.d.ts +5 -0
  30. package/dist/chat/plugins/task-message.d.ts +23 -0
  31. package/dist/chat/plugins/task-queue.d.ts +5 -0
  32. package/dist/chat/plugins/task-runner.d.ts +12 -0
  33. package/dist/chat/plugins/task-signing.d.ts +31 -0
  34. package/dist/chat/plugins/types.d.ts +1 -0
  35. package/dist/chat/plugins/validation.d.ts +5 -0
  36. package/dist/chat/prompt.d.ts +15 -1
  37. package/dist/chat/requester.d.ts +6 -5
  38. package/dist/chat/respond-helpers.d.ts +2 -0
  39. package/dist/chat/respond.d.ts +13 -2
  40. package/dist/chat/runtime/agent-continue-runner.d.ts +4 -0
  41. package/dist/chat/runtime/reply-executor.d.ts +5 -1
  42. package/dist/chat/runtime/slack-resume.d.ts +10 -2
  43. package/dist/chat/runtime/slack-runtime.d.ts +6 -1
  44. package/dist/chat/sandbox/egress-credentials.d.ts +8 -8
  45. package/dist/chat/sandbox/sandbox.d.ts +2 -2
  46. package/dist/chat/sentry.d.ts +1 -0
  47. package/dist/chat/services/mcp-auth-orchestration.d.ts +2 -1
  48. package/dist/chat/services/plugin-auth-orchestration.d.ts +2 -1
  49. package/dist/chat/services/subscribed-decision.d.ts +2 -2
  50. package/dist/chat/services/turn-session-record.d.ts +11 -7
  51. package/dist/chat/sql/db.d.ts +3 -0
  52. package/dist/chat/sql/executor.d.ts +7 -0
  53. package/dist/chat/sql/neon.d.ts +2 -4
  54. package/dist/chat/sql/postgres.d.ts +6 -0
  55. package/dist/chat/state/turn-session.d.ts +8 -5
  56. package/dist/chat/task-execution/state.d.ts +7 -2
  57. package/dist/chat/task-execution/worker.d.ts +1 -1
  58. package/dist/chat/tools/agent-tools.d.ts +9 -2
  59. package/dist/chat/tools/slack/context.d.ts +2 -2
  60. package/dist/chat/tools/types.d.ts +7 -4
  61. package/dist/chat/vercel-queue-client.d.ts +3 -0
  62. package/dist/{chunk-YOHFWWBV.js → chunk-2ECJXSVQ.js} +5 -107
  63. package/dist/{chunk-OR6NQJ5E.js → chunk-4SCWV7TJ.js} +3 -3
  64. package/dist/chunk-4UO6FK4G.js +64 -0
  65. package/dist/chunk-56TBVRJG.js +115 -0
  66. package/dist/{chunk-3BYAPS6B.js → chunk-EJN6G5A2.js} +17 -11
  67. package/dist/{chunk-SQGMG7OD.js → chunk-HHDUKWVG.js} +508 -149
  68. package/dist/{chunk-6UP2Z2RZ.js → chunk-JBASI5VV.js} +7 -7
  69. package/dist/chunk-KNFROR7R.js +127 -0
  70. package/dist/{chunk-HYHKTFG2.js → chunk-KOIMO7S3.js} +186 -910
  71. package/dist/chunk-MLKGABMK.js +9 -0
  72. package/dist/chunk-NFTMTIP3.js +964 -0
  73. package/dist/chunk-NYKJ3KON.js +1082 -0
  74. package/dist/{chunk-SJHUF3DP.js → chunk-OJ53FYVG.js} +2 -10
  75. package/dist/{chunk-KVZL5NZS.js → chunk-Q3XNY442.js} +17 -7
  76. package/dist/{chunk-YRDS7VKO.js → chunk-Q6XFTRV5.js} +2 -2
  77. package/dist/chunk-R6Z5XWY3.js +1076 -0
  78. package/dist/chunk-RV5RYIJW.js +56 -0
  79. package/dist/chunk-SG5WAA7H.js +132 -0
  80. package/dist/chunk-ST6YNAXG.js +54 -0
  81. package/dist/{chunk-GM7HTXYC.js → chunk-T77LUIX3.js} +148 -151
  82. package/dist/{chunk-CYUI7JU5.js → chunk-VALUBQ7R.js} +22 -30
  83. package/dist/chunk-XBBC6W45.js +71 -0
  84. package/dist/chunk-Y2CM7HXH.js +111 -0
  85. package/dist/{chunk-F6HWCPOC.js → chunk-Y5OFBCBZ.js} +1 -1
  86. package/dist/{chunk-M4FLLXXD.js → chunk-Z4CIQ3EB.js} +5 -1
  87. package/dist/{chunk-7Q5YOUUT.js → chunk-ZLMBNBUG.js} +146 -52
  88. package/dist/{chunk-2LUZA3LY.js → chunk-ZQB37HUX.js} +11 -11
  89. package/dist/cli/chat.js +87 -8
  90. package/dist/cli/check.js +8 -7
  91. package/dist/cli/env.js +4 -53
  92. package/dist/cli/init.js +6 -1
  93. package/dist/cli/main.js +84 -0
  94. package/dist/cli/plugins.js +244 -0
  95. package/dist/cli/run.js +5 -52
  96. package/dist/cli/snapshot-warmup.js +12 -11
  97. package/dist/cli/upgrade.js +385 -26
  98. package/dist/db-7A7PFRGL.js +17 -0
  99. package/dist/deployment.d.ts +1 -0
  100. package/dist/handlers/sandbox-egress-route.d.ts +4 -0
  101. package/dist/handlers/slack-webhook.d.ts +4 -0
  102. package/dist/handlers/webhooks.d.ts +6 -13
  103. package/dist/instrumentation.js +14 -18
  104. package/dist/nitro.d.ts +1 -1
  105. package/dist/nitro.js +67 -101
  106. package/dist/plugin-module.d.ts +21 -0
  107. package/dist/plugins-PZMDS7AT.js +15 -0
  108. package/dist/plugins.d.ts +9 -5
  109. package/dist/registry-OIPAJU2O.js +46 -0
  110. package/dist/reporting/conversations.d.ts +3 -3
  111. package/dist/reporting.d.ts +6 -5
  112. package/dist/reporting.js +42 -28
  113. package/dist/{runner-27NP2TEO.js → runner-KPLNHDCV.js} +77 -19
  114. package/dist/sentry-4CP5NNQ5.js +31 -0
  115. package/dist/validation-SLA6IGF7.js +15 -0
  116. package/dist/vercel.js +1 -1
  117. package/package.json +14 -11
  118. package/dist/chat/conversations/configured.d.ts +0 -5
  119. package/dist/chat/conversations/state.d.ts +0 -4
  120. package/dist/chunk-2KG3PWR4.js +0 -17
  121. package/dist/chunk-JL2SLRAT.js +0 -1970
@@ -1,31 +1,62 @@
1
+ import {
2
+ JUNIOR_THREAD_STATE_TTL_MS,
3
+ buildConversationStatePatch
4
+ } from "./chunk-Z4CIQ3EB.js";
5
+ import {
6
+ buildNonInteractiveShellScript,
7
+ createSandboxInstance,
8
+ getRuntimeDependencyProfileHash,
9
+ getVercelSandboxCredentials,
10
+ isSnapshotMissingError,
11
+ resolveRuntimeDependencySnapshot,
12
+ runNonInteractiveCommand
13
+ } from "./chunk-JBASI5VV.js";
14
+ import {
15
+ pluginTaskId,
16
+ pluginTaskParamsSchema,
17
+ sendVercelPluginTask
18
+ } from "./chunk-KNFROR7R.js";
1
19
  import {
2
20
  JUNIOR_PERSONALITY,
3
21
  abandonAgentTurnSessionRecord,
22
+ buildPluginSystemPromptContributions,
4
23
  buildSystemPrompt,
5
24
  buildTurnContextPrompt,
6
- createAgentPluginHookRunner,
7
25
  escapeXml,
8
- getAgentPluginTools,
9
26
  getAgentTurnSessionRecord,
10
- getSlackToolContext,
11
27
  loadConnectedMcpProviders,
12
28
  recordAuthorizationRequested,
13
29
  recordMcpProviderConnected,
14
- resolveChannelCapabilities,
15
30
  upsertAgentTurnSessionRecord
16
- } from "./chunk-HYHKTFG2.js";
31
+ } from "./chunk-KOIMO7S3.js";
17
32
  import {
18
- discoverSkills,
19
- findSkillByName,
20
- loadSkillsByName,
21
- parseSkillInvocation
22
- } from "./chunk-OR6NQJ5E.js";
33
+ createPluginEmbedder,
34
+ createPluginHookRunner,
35
+ createPluginModel,
36
+ getPluginSystemPromptContributions,
37
+ getPluginTools,
38
+ getPluginUserPromptContributions,
39
+ getPlugins,
40
+ getSlackToolContext,
41
+ resolveChannelCapabilities
42
+ } from "./chunk-NFTMTIP3.js";
23
43
  import {
24
- buildConversationStatePatch
25
- } from "./chunk-M4FLLXXD.js";
44
+ createPluginLogger,
45
+ createPluginState
46
+ } from "./chunk-56TBVRJG.js";
47
+ import {
48
+ getDb
49
+ } from "./chunk-NYKJ3KON.js";
50
+ import {
51
+ SANDBOX_DATA_ROOT,
52
+ SANDBOX_SKILLS_ROOT,
53
+ SANDBOX_WORKSPACE_ROOT,
54
+ sandboxSkillDir,
55
+ sandboxSkillFile
56
+ } from "./chunk-G3E7SCME.js";
26
57
  import {
27
- JUNIOR_THREAD_STATE_TTL_MS
28
- } from "./chunk-JL2SLRAT.js";
58
+ getStateAdapter
59
+ } from "./chunk-Y5OFBCBZ.js";
29
60
  import {
30
61
  SlackActionError,
31
62
  downloadPrivateSlackFile,
@@ -36,43 +67,7 @@ import {
36
67
  normalizeSlackConversationId,
37
68
  parseDestination,
38
69
  withSlackRetries
39
- } from "./chunk-YRDS7VKO.js";
40
- import {
41
- buildNonInteractiveShellScript,
42
- createSandboxInstance,
43
- getRuntimeDependencyProfileHash,
44
- getVercelSandboxCredentials,
45
- isSnapshotMissingError,
46
- resolveRuntimeDependencySnapshot,
47
- runNonInteractiveCommand
48
- } from "./chunk-6UP2Z2RZ.js";
49
- import {
50
- SANDBOX_DATA_ROOT,
51
- SANDBOX_SKILLS_ROOT,
52
- SANDBOX_WORKSPACE_ROOT,
53
- sandboxSkillDir,
54
- sandboxSkillFile
55
- } from "./chunk-G3E7SCME.js";
56
- import {
57
- createPluginBroker,
58
- credentialContextSchema,
59
- getPluginCapabilityProviders,
60
- getPluginCatalogSignature,
61
- getPluginDefinition,
62
- getPluginDisplayName,
63
- getPluginMcpProviders,
64
- getPluginOAuthConfig,
65
- getPluginProviders,
66
- isPluginConfigKey,
67
- resolveAuthTokenPlaceholder,
68
- resolvePluginCommandEnv
69
- } from "./chunk-7Q5YOUUT.js";
70
- import {
71
- listReferenceFiles
72
- } from "./chunk-KVZL5NZS.js";
73
- import {
74
- getStateAdapter
75
- } from "./chunk-F6HWCPOC.js";
70
+ } from "./chunk-Q6XFTRV5.js";
76
71
  import {
77
72
  GEN_AI_PROVIDER_NAME,
78
73
  GEN_AI_SERVER_ADDRESS,
@@ -85,7 +80,7 @@ import {
85
80
  encodeNonImageAttachmentForPrompt,
86
81
  extractAssistantText,
87
82
  getGatewayApiKey,
88
- getPiGatewayApiKeyOverride,
83
+ getPiGatewayApiKey,
89
84
  getPiMessageRole,
90
85
  getSessionIdentifiers,
91
86
  getTerminalAssistantMessages,
@@ -100,9 +95,9 @@ import {
100
95
  normalizeSlackEmojiName,
101
96
  normalizeToolNameFromResult,
102
97
  parseSlackThreadId,
103
- prependMissingRuntimeTurnContext,
104
98
  resolveConversationPrivacy,
105
99
  resolveGatewayModel,
100
+ stripRuntimeTurnContext,
106
101
  summarizeMessageText,
107
102
  toGenAiMessageMetadata,
108
103
  toGenAiMessagesTraceAttributes,
@@ -112,12 +107,34 @@ import {
112
107
  toObservablePromptPart,
113
108
  trimTrailingAssistantMessages,
114
109
  upsertActiveSkill
115
- } from "./chunk-GM7HTXYC.js";
110
+ } from "./chunk-T77LUIX3.js";
111
+ import {
112
+ discoverSkills,
113
+ findSkillByName,
114
+ loadSkillsByName,
115
+ parseSkillInvocation
116
+ } from "./chunk-4SCWV7TJ.js";
117
+ import {
118
+ createPluginBroker,
119
+ credentialContextSchema,
120
+ getPluginCapabilityProviders,
121
+ getPluginCatalogSignature,
122
+ getPluginDefinition,
123
+ getPluginDisplayName,
124
+ getPluginMcpProviders,
125
+ getPluginOAuthConfig,
126
+ getPluginProviders,
127
+ isPluginConfigKey,
128
+ resolveAuthTokenPlaceholder,
129
+ resolvePluginCommandEnv
130
+ } from "./chunk-ZLMBNBUG.js";
116
131
  import {
117
132
  createRequester,
118
- parseActorUserId,
119
- toStoredSlackRequester
120
- } from "./chunk-CYUI7JU5.js";
133
+ parseActorUserId
134
+ } from "./chunk-VALUBQ7R.js";
135
+ import {
136
+ listReferenceFiles
137
+ } from "./chunk-Q3XNY442.js";
121
138
  import {
122
139
  extractGenAiUsageAttributes,
123
140
  extractGenAiUsageSummary,
@@ -136,10 +153,11 @@ import {
136
153
  setTags,
137
154
  toOptionalString,
138
155
  withSpan
139
- } from "./chunk-3BYAPS6B.js";
156
+ } from "./chunk-EJN6G5A2.js";
140
157
  import {
141
- sentry_exports
142
- } from "./chunk-SJHUF3DP.js";
158
+ startInactiveSpan,
159
+ withActiveSpan
160
+ } from "./chunk-ST6YNAXG.js";
143
161
 
144
162
  // src/chat/configuration/defaults.ts
145
163
  var installDefaults = {};
@@ -724,31 +742,65 @@ var ProviderCredentialRouter = class {
724
742
  }
725
743
  };
726
744
 
745
+ // src/chat/credentials/user-token-store.ts
746
+ import {
747
+ pluginStoredTokensSchema
748
+ } from "@sentry/junior-plugin-api";
749
+ var storedTokensSchema = pluginStoredTokensSchema;
750
+
727
751
  // src/chat/credentials/state-adapter-token-store.ts
728
752
  var KEY_PREFIX = "oauth-token";
729
753
  var BUFFER_MS = 24 * 60 * 60 * 1e3;
730
754
  var LONG_LIVED_TTL_MS = 365 * 24 * 60 * 60 * 1e3;
755
+ var REFRESH_LOCK_TTL_MS = 3e4;
756
+ var REFRESH_LOCK_WAIT_MS = 3e4;
757
+ var REFRESH_LOCK_RETRY_MS = 100;
731
758
  function tokenKey(userId, provider) {
732
759
  return `${KEY_PREFIX}:${userId}:${provider}`;
733
760
  }
761
+ function refreshLockKey(userId, provider) {
762
+ return `${tokenKey(userId, provider)}:refresh`;
763
+ }
764
+ async function sleep(ms) {
765
+ await new Promise((resolve) => setTimeout(resolve, ms));
766
+ }
734
767
  var StateAdapterTokenStore = class {
735
768
  state;
736
769
  constructor(stateAdapter) {
737
770
  this.state = stateAdapter;
738
771
  }
739
772
  async get(userId, provider) {
740
- const stored = await this.state.get(
741
- tokenKey(userId, provider)
742
- );
743
- return stored ?? void 0;
773
+ const stored = await this.state.get(tokenKey(userId, provider));
774
+ return stored === null || stored === void 0 ? void 0 : storedTokensSchema.parse(stored);
744
775
  }
745
776
  async set(userId, provider, tokens) {
746
- const ttlMs = tokens.expiresAt ? Math.max(tokens.expiresAt - Date.now() + BUFFER_MS, BUFFER_MS) : LONG_LIVED_TTL_MS;
747
- await this.state.set(tokenKey(userId, provider), tokens, ttlMs);
777
+ const parsed = storedTokensSchema.parse(tokens);
778
+ const expiresAt = parsed.refreshTokenExpiresAt ?? parsed.expiresAt;
779
+ const ttlMs = expiresAt ? Math.max(expiresAt - Date.now() + BUFFER_MS, BUFFER_MS) : LONG_LIVED_TTL_MS;
780
+ await this.state.set(tokenKey(userId, provider), parsed, ttlMs);
748
781
  }
749
782
  async delete(userId, provider) {
750
783
  await this.state.delete(tokenKey(userId, provider));
751
784
  }
785
+ /** Wait for the per-slot refresh gate so rotated refresh tokens are used once. */
786
+ async withRefresh(userId, provider, callback) {
787
+ const lockKey = refreshLockKey(userId, provider);
788
+ const deadline = Date.now() + REFRESH_LOCK_WAIT_MS;
789
+ while (true) {
790
+ const lock = await this.state.acquireLock(lockKey, REFRESH_LOCK_TTL_MS);
791
+ if (lock) {
792
+ try {
793
+ return await callback();
794
+ } finally {
795
+ await this.state.releaseLock(lock);
796
+ }
797
+ }
798
+ if (Date.now() >= deadline) {
799
+ throw new Error(`Could not acquire OAuth token refresh lock`);
800
+ }
801
+ await sleep(REFRESH_LOCK_RETRY_MS);
802
+ }
803
+ }
752
804
  };
753
805
 
754
806
  // src/chat/capabilities/factory.ts
@@ -1008,18 +1060,48 @@ ${usage}
1008
1060
  exitCode: 2
1009
1061
  });
1010
1062
  }
1063
+ async function handlePluginsCommand(args) {
1064
+ const usage = "jr-rpc plugins list";
1065
+ const subverb = (args[0] ?? "").trim();
1066
+ if (subverb !== "list" || args.length !== 1) {
1067
+ return commandResult({
1068
+ stderr: `Usage:
1069
+ ${usage}
1070
+ `,
1071
+ exitCode: 2
1072
+ });
1073
+ }
1074
+ const plugins = getPluginProviders().map((plugin) => ({
1075
+ name: plugin.manifest.name,
1076
+ displayName: plugin.manifest.displayName,
1077
+ description: plugin.manifest.description,
1078
+ capabilities: [...plugin.manifest.capabilities],
1079
+ configKeys: [...plugin.manifest.configKeys]
1080
+ })).sort((left, right) => left.name.localeCompare(right.name));
1081
+ return commandResult({
1082
+ stdout: {
1083
+ ok: true,
1084
+ plugins
1085
+ },
1086
+ exitCode: 0
1087
+ });
1088
+ }
1011
1089
  function createJrRpcCommand(deps) {
1012
1090
  return defineCommand("jr-rpc", async (args) => {
1013
1091
  const usage = [
1014
1092
  "jr-rpc config get <key>",
1015
1093
  "jr-rpc config set <key> <value> [--json]",
1016
1094
  "jr-rpc config unset <key>",
1017
- "jr-rpc config list [--prefix <value>]"
1095
+ "jr-rpc config list [--prefix <value>]",
1096
+ "jr-rpc plugins list"
1018
1097
  ].join("\n");
1019
1098
  const verb = (args[0] ?? "").trim();
1020
1099
  if (verb === "config") {
1021
1100
  return handleConfigCommand(args.slice(1), deps);
1022
1101
  }
1102
+ if (verb === "plugins") {
1103
+ return handlePluginsCommand(args.slice(1));
1104
+ }
1023
1105
  return commandResult({
1024
1106
  stderr: `Unsupported jr-rpc command. Use:
1025
1107
  ${usage}
@@ -5874,8 +5956,9 @@ function createAdvisorTool(context) {
5874
5956
  "Advisor guidance is unavailable because advisor history could not be loaded. Continue without assuming advisor history."
5875
5957
  );
5876
5958
  }
5959
+ const apiKeyOverride = getPiGatewayApiKey();
5877
5960
  const advisorAgent = new Agent({
5878
- getApiKey: () => getPiGatewayApiKeyOverride(),
5961
+ ...apiKeyOverride ? { getApiKey: () => apiKeyOverride } : {},
5879
5962
  initialState: {
5880
5963
  systemPrompt: ADVISOR_SYSTEM_PROMPT,
5881
5964
  model: resolveGatewayModel(context.config.modelId),
@@ -6685,6 +6768,7 @@ function createTools(availableSkills, hooks = {}, context) {
6685
6768
  tools.slackListUpdateItem = createSlackListUpdateItemTool(state);
6686
6769
  const outputChannelId = slackContext.destinationChannelId;
6687
6770
  const outputCapabilities = outputChannelId ? resolveChannelCapabilities(outputChannelId) : void 0;
6771
+ const canPostStandaloneSlackMessage = context.surface === void 0 || context.surface === "slack";
6688
6772
  const rawChannelCapabilities = resolveChannelCapabilities(
6689
6773
  slackContext.sourceChannelId
6690
6774
  );
@@ -6694,11 +6778,13 @@ function createTools(availableSkills, hooks = {}, context) {
6694
6778
  state
6695
6779
  );
6696
6780
  }
6697
- if (outputCapabilities?.canPostToChannel) {
6781
+ if (outputCapabilities?.canPostToChannel && canPostStandaloneSlackMessage) {
6698
6782
  tools.slackChannelPostMessage = createSlackChannelPostMessageTool(
6699
6783
  slackContext,
6700
6784
  state
6701
6785
  );
6786
+ }
6787
+ if (outputCapabilities?.canPostToChannel) {
6702
6788
  tools.slackChannelListMessages = createSlackChannelListMessagesTool(slackContext);
6703
6789
  }
6704
6790
  if (rawChannelCapabilities.canAddReactions) {
@@ -6708,9 +6794,7 @@ function createTools(availableSkills, hooks = {}, context) {
6708
6794
  );
6709
6795
  }
6710
6796
  }
6711
- for (const [name, pluginTool] of Object.entries(
6712
- getAgentPluginTools(context)
6713
- )) {
6797
+ for (const [name, pluginTool] of Object.entries(getPluginTools(context))) {
6714
6798
  if (tools[name]) {
6715
6799
  throw new Error(`Plugin tool "${name}" conflicts with a core tool`);
6716
6800
  }
@@ -6784,7 +6868,7 @@ function createTracedStreamFn(baseOrOptions = streamSimple) {
6784
6868
  const conversationPrivacy = typeof baseOrOptions === "function" ? void 0 : baseOrOptions.conversationPrivacy;
6785
6869
  const effectivePrivacy = conversationPrivacy ?? "private";
6786
6870
  return async (model, context, options) => {
6787
- const span = sentry_exports.startInactiveSpan({
6871
+ const span = startInactiveSpan({
6788
6872
  name: `chat ${model.id}`,
6789
6873
  op: "gen_ai.chat",
6790
6874
  attributes: {
@@ -6793,7 +6877,7 @@ function createTracedStreamFn(baseOrOptions = streamSimple) {
6793
6877
  }
6794
6878
  });
6795
6879
  try {
6796
- const stream = await sentry_exports.withActiveSpan(
6880
+ const stream = await withActiveSpan(
6797
6881
  span,
6798
6882
  () => Promise.resolve(base(model, context, options))
6799
6883
  );
@@ -6829,6 +6913,9 @@ import fs3 from "fs/promises";
6829
6913
 
6830
6914
  // src/chat/oauth-flow.ts
6831
6915
  import { randomBytes } from "crypto";
6916
+ import {
6917
+ sourceSchema
6918
+ } from "@sentry/junior-plugin-api";
6832
6919
  var OAUTH_STATE_TTL_MS = 10 * 60 * 1e3;
6833
6920
  function optionalString(value) {
6834
6921
  return typeof value === "string" ? value : void 0;
@@ -6844,13 +6931,22 @@ function parseOAuthStatePayload(value) {
6844
6931
  if (value.destination !== void 0 && !destination) {
6845
6932
  return void 0;
6846
6933
  }
6934
+ const source = value.source === void 0 ? void 0 : sourceSchema.safeParse(value.source);
6935
+ if (value.source !== void 0 && (!source || !source.success)) {
6936
+ return void 0;
6937
+ }
6938
+ const pendingMessage = optionalString(value.pendingMessage);
6939
+ if (pendingMessage && !source?.success) {
6940
+ return void 0;
6941
+ }
6847
6942
  return {
6848
6943
  userId: value.userId,
6849
6944
  provider: value.provider,
6850
6945
  ...optionalString(value.channelId) ? { channelId: optionalString(value.channelId) } : {},
6851
6946
  ...destination ? { destination } : {},
6947
+ ...source?.success ? { source: source.data } : {},
6852
6948
  ...optionalString(value.threadTs) ? { threadTs: optionalString(value.threadTs) } : {},
6853
- ...optionalString(value.pendingMessage) ? { pendingMessage: optionalString(value.pendingMessage) } : {},
6949
+ ...pendingMessage ? { pendingMessage } : {},
6854
6950
  ...isRecord(value.configuration) ? { configuration: value.configuration } : {},
6855
6951
  ...optionalString(value.resumeConversationId) ? { resumeConversationId: optionalString(value.resumeConversationId) } : {},
6856
6952
  ...optionalString(value.resumeSessionId) ? { resumeSessionId: optionalString(value.resumeSessionId) } : {},
@@ -6972,6 +7068,7 @@ async function startOAuthFlow(provider, input) {
6972
7068
  provider,
6973
7069
  ...input.channelId ? { channelId: input.channelId } : {},
6974
7070
  ...input.destination ? { destination: input.destination } : {},
7071
+ ...input.source ? { source: input.source } : {},
6975
7072
  ...input.threadTs ? { threadTs: input.threadTs } : {},
6976
7073
  ...input.userMessage ? { pendingMessage: input.userMessage } : {},
6977
7074
  ...configuration && Object.keys(configuration).length > 0 ? { configuration } : {},
@@ -7021,16 +7118,16 @@ import { createHmac, randomUUID, timingSafeEqual } from "crypto";
7021
7118
  // src/chat/sandbox/egress-schemas.ts
7022
7119
  import { z } from "zod";
7023
7120
  import {
7024
- agentPluginAuthorizationSchema,
7025
- agentPluginCredentialHeaderTransformSchema,
7026
- agentPluginGrantSchema,
7027
- agentPluginProviderAccountSchema
7121
+ pluginAuthorizationSchema,
7122
+ pluginCredentialHeaderTransformSchema,
7123
+ pluginGrantSchema,
7124
+ pluginProviderAccountSchema
7028
7125
  } from "@sentry/junior-plugin-api";
7029
7126
  var finiteNumberSchema = z.number().refine(Number.isFinite);
7030
7127
  var httpStatusSchema = z.number().int().min(100).max(599);
7031
7128
  var providerNameSchema = z.string().regex(/^[a-z][a-z0-9-]*$/);
7032
7129
  var credentialSignalKindSchema = z.enum(["auth_required", "unavailable"]);
7033
- var sandboxEgressGrantSchema = agentPluginGrantSchema;
7130
+ var sandboxEgressGrantSchema = pluginGrantSchema;
7034
7131
  var sandboxEgressCredentialContextSchema = z.object({
7035
7132
  credentials: credentialContextSchema,
7036
7133
  egressId: z.string().min(1),
@@ -7038,15 +7135,15 @@ var sandboxEgressCredentialContextSchema = z.object({
7038
7135
  contextId: z.string().min(1)
7039
7136
  }).strict();
7040
7137
  var sandboxEgressCredentialLeaseSchema = z.object({
7041
- account: agentPluginProviderAccountSchema.optional(),
7042
- authorization: agentPluginAuthorizationSchema.optional(),
7138
+ account: pluginProviderAccountSchema.optional(),
7139
+ authorization: pluginAuthorizationSchema.optional(),
7043
7140
  grant: sandboxEgressGrantSchema,
7044
7141
  provider: providerNameSchema,
7045
7142
  expiresAt: z.string().min(1),
7046
- headerTransforms: z.array(agentPluginCredentialHeaderTransformSchema).min(1)
7143
+ headerTransforms: z.array(pluginCredentialHeaderTransformSchema).min(1)
7047
7144
  }).strict();
7048
7145
  var sandboxEgressAuthRequiredSignalSchema = z.object({
7049
- authorization: agentPluginAuthorizationSchema.optional(),
7146
+ authorization: pluginAuthorizationSchema.optional(),
7050
7147
  grant: sandboxEgressGrantSchema,
7051
7148
  kind: credentialSignalKindSchema.default("auth_required"),
7052
7149
  provider: providerNameSchema,
@@ -7062,7 +7159,7 @@ var sandboxEgressAuthRequiredSignalSchema = z.object({
7062
7159
  }
7063
7160
  });
7064
7161
  var sandboxEgressPermissionDeniedSignalSchema = z.object({
7065
- account: agentPluginProviderAccountSchema.optional(),
7162
+ account: pluginProviderAccountSchema.optional(),
7066
7163
  acceptedPermissions: z.string().optional(),
7067
7164
  grant: sandboxEgressGrantSchema,
7068
7165
  message: z.string().min(1),
@@ -7808,7 +7905,7 @@ function truncateOutput(output, maxLength) {
7808
7905
  truncated: true
7809
7906
  };
7810
7907
  }
7811
- function sleep(ms) {
7908
+ function sleep2(ms) {
7812
7909
  return new Promise((resolve) => {
7813
7910
  setTimeout(resolve, ms);
7814
7911
  });
@@ -7979,7 +8076,7 @@ function createSandboxSessionManager(options) {
7979
8076
  if (!isSnapshottingError(error) || attempt === SNAPSHOT_BOOT_RETRY_COUNT - 1) {
7980
8077
  throw error;
7981
8078
  }
7982
- await sleep(SNAPSHOT_BOOT_RETRY_DELAY_MS);
8079
+ await sleep2(SNAPSHOT_BOOT_RETRY_DELAY_MS);
7983
8080
  }
7984
8081
  }
7985
8082
  throw new Error(`Failed to boot sandbox from snapshot ${snapshotId}`);
@@ -9100,12 +9197,15 @@ function normalizeToolResult(result, isSandboxResult) {
9100
9197
  }
9101
9198
 
9102
9199
  // src/chat/tools/execution/tool-error-handler.ts
9103
- import { AgentPluginToolInputError } from "@sentry/junior-plugin-api";
9200
+ import { PluginToolInputError } from "@sentry/junior-plugin-api";
9104
9201
 
9105
9202
  // src/chat/services/plugin-auth-orchestration.ts
9106
9203
  import { THREAD_STATE_TTL_MS } from "chat";
9107
9204
 
9108
9205
  // src/chat/mcp/auth-store.ts
9206
+ import {
9207
+ sourceSchema as sourceSchema2
9208
+ } from "@sentry/junior-plugin-api";
9109
9209
  var MCP_AUTH_SESSION_PREFIX = "junior:mcp_auth_session";
9110
9210
  var MCP_AUTH_CREDENTIALS_PREFIX = "junior:mcp_auth_credentials";
9111
9211
  var MCP_AUTH_SESSION_INDEX_PREFIX = "junior:mcp_auth_session_index";
@@ -9157,12 +9257,17 @@ function parseMcpAuthSession(value) {
9157
9257
  if (parsed.destination !== void 0 && !destination) {
9158
9258
  return void 0;
9159
9259
  }
9260
+ const source = parsed.source === void 0 ? void 0 : sourceSchema2.safeParse(parsed.source);
9261
+ if (parsed.source !== void 0 && (!source || !source.success)) {
9262
+ return void 0;
9263
+ }
9160
9264
  return {
9161
9265
  authSessionId: parsed.authSessionId,
9162
9266
  provider: parsed.provider,
9163
9267
  userId: parsed.userId,
9164
9268
  conversationId: parsed.conversationId,
9165
9269
  ...destination ? { destination } : {},
9270
+ ...source?.success ? { source: source.data } : {},
9166
9271
  sessionId: parsed.sessionId,
9167
9272
  userMessage: parsed.userMessage,
9168
9273
  createdAtMs: parsed.createdAtMs,
@@ -9483,6 +9588,7 @@ function createPluginAuthOrchestration(input) {
9483
9588
  requesterId: input.requesterId,
9484
9589
  channelId: input.channelId,
9485
9590
  destination: input.destination,
9591
+ source: input.source,
9486
9592
  threadTs: input.threadTs,
9487
9593
  userMessage: input.userMessage,
9488
9594
  channelConfiguration: input.channelConfiguration,
@@ -9580,7 +9686,7 @@ function createPluginAuthOrchestration(input) {
9580
9686
 
9581
9687
  // src/chat/tools/execution/tool-error-handler.ts
9582
9688
  function isPluginToolInputError(error) {
9583
- return error instanceof AgentPluginToolInputError || error instanceof Error && error.name === "AgentPluginToolInputError";
9689
+ return error instanceof PluginToolInputError || error instanceof Error && error.name === "PluginToolInputError";
9584
9690
  }
9585
9691
  function getToolErrorType(error) {
9586
9692
  if (error instanceof McpToolError) return "tool_error";
@@ -9661,12 +9767,27 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, trac
9661
9767
  }
9662
9768
 
9663
9769
  // src/chat/tools/agent-tools.ts
9664
- function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor, pluginAuthOrchestration, onToolCall, agentHooks, conversationPrivacy) {
9770
+ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor, pluginAuthOrchestration, onToolCall, agentHooks, conversationPrivacy, onToolResult) {
9665
9771
  const shouldTrace = shouldEmitDevAgentTrace();
9666
9772
  const effectiveConversationPrivacy = conversationPrivacy ?? "private";
9667
9773
  const serializeToolPayload = (payload) => serializeGenAiAttribute(
9668
9774
  effectiveConversationPrivacy === "private" ? toGenAiPayloadMetadata(payload) : payload
9669
9775
  );
9776
+ const notifyToolResult = async (report) => {
9777
+ try {
9778
+ await onToolResult?.(report);
9779
+ } catch (error) {
9780
+ logWarn(
9781
+ "tool_result_observer_failed",
9782
+ spanContext,
9783
+ {
9784
+ "gen_ai.tool.name": report.toolName,
9785
+ "exception.message": error instanceof Error ? error.message : String(error)
9786
+ },
9787
+ "Tool result observer failed"
9788
+ );
9789
+ }
9790
+ };
9670
9791
  return Object.entries(tools).map(([toolName, toolDef]) => ({
9671
9792
  name: toolName,
9672
9793
  label: toolName,
@@ -9716,7 +9837,7 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
9716
9837
  input: parsed
9717
9838
  }) : { input: parsed, env: {} };
9718
9839
  const toolInput = beforeTool.input;
9719
- onToolCall?.(toolName, toolInput);
9840
+ await onToolCall?.(toolName, toolInput);
9720
9841
  const sandboxInput = buildSandboxInput(toolName, toolInput);
9721
9842
  const isSandbox = Boolean(sandboxExecutor?.canExecute(toolName));
9722
9843
  const result = isSandbox ? await sandboxExecutor.execute({
@@ -9731,7 +9852,9 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
9731
9852
  });
9732
9853
  const normalized = normalizeToolResult(result, isSandbox);
9733
9854
  if (isSandbox && pluginAuthOrchestration) {
9734
- await pluginAuthOrchestration.maybeHandleAuthSignal(normalized.details);
9855
+ await pluginAuthOrchestration.maybeHandleAuthSignal(
9856
+ normalized.details
9857
+ );
9735
9858
  }
9736
9859
  const resultAttributeValue = normalized.details && typeof normalized.details === "object" && "rawResult" in normalized.details && normalized.details.rawResult !== void 0 ? normalized.details.rawResult : normalized.details;
9737
9860
  const toolResultAttribute = serializeToolPayload(resultAttributeValue);
@@ -9744,8 +9867,20 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
9744
9867
  )
9745
9868
  });
9746
9869
  }
9870
+ await notifyToolResult({
9871
+ ok: true,
9872
+ params: toolInput,
9873
+ result: resultAttributeValue,
9874
+ toolName
9875
+ });
9747
9876
  return normalized;
9748
9877
  } catch (error) {
9878
+ await notifyToolResult({
9879
+ error: error instanceof Error ? error.message : String(error),
9880
+ ok: false,
9881
+ params: parsed,
9882
+ toolName
9883
+ });
9749
9884
  if (error instanceof AuthorizationPauseError || error instanceof AuthorizationFlowDisabledError) {
9750
9885
  throw error;
9751
9886
  }
@@ -10352,7 +10487,7 @@ async function loadTurnSessionRecord(ctx) {
10352
10487
  const canUseTurnSession = Boolean(ctx.conversationId && ctx.sessionId);
10353
10488
  const existingSessionRecord = canUseTurnSession && ctx.conversationId && ctx.sessionId ? await getAgentTurnSessionRecord(ctx.conversationId, ctx.sessionId) : void 0;
10354
10489
  const hasAwaitingResumeRecord = Boolean(
10355
- existingSessionRecord && existingSessionRecord.state === "awaiting_resume" && existingSessionRecord.piMessages.length > 0
10490
+ existingSessionRecord && existingSessionRecord.state === "awaiting_resume"
10356
10491
  );
10357
10492
  return {
10358
10493
  canUseTurnSession,
@@ -10376,6 +10511,7 @@ async function persistRunningSessionRecord(args) {
10376
10511
  cumulativeDurationMs: latestSessionRecord?.cumulativeDurationMs,
10377
10512
  cumulativeUsage: latestSessionRecord?.cumulativeUsage,
10378
10513
  ...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
10514
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10379
10515
  sessionId: args.sessionId,
10380
10516
  sliceId: args.sliceId,
10381
10517
  state: "running",
@@ -10420,6 +10556,7 @@ async function persistCompletedSessionRecord(args) {
10420
10556
  args.currentUsage
10421
10557
  ),
10422
10558
  ...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
10559
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10423
10560
  sessionId: args.sessionId,
10424
10561
  sliceId: args.sliceId,
10425
10562
  state: "completed",
@@ -10455,7 +10592,7 @@ async function persistAuthPauseSessionRecord(args) {
10455
10592
  args.messages,
10456
10593
  latestSessionRecord?.piMessages
10457
10594
  );
10458
- if (piMessages.length === 0 || !isContinuableBoundary(piMessages)) {
10595
+ if (piMessages.length > 0 && !isContinuableBoundary(piMessages)) {
10459
10596
  return void 0;
10460
10597
  }
10461
10598
  return await upsertAgentTurnSessionRecord({
@@ -10470,6 +10607,7 @@ async function persistAuthPauseSessionRecord(args) {
10470
10607
  args.currentUsage
10471
10608
  ),
10472
10609
  ...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
10610
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10473
10611
  sessionId: args.sessionId,
10474
10612
  sliceId: nextSliceId,
10475
10613
  state: "awaiting_resume",
@@ -10529,6 +10667,7 @@ async function persistTimeoutSessionRecord(args) {
10529
10667
  ...args.destination ?? latestSessionRecord?.destination ? {
10530
10668
  destination: args.destination ?? latestSessionRecord?.destination
10531
10669
  } : {},
10670
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10532
10671
  sessionId: args.sessionId,
10533
10672
  sliceId: args.currentSliceId,
10534
10673
  state: "failed",
@@ -10548,6 +10687,7 @@ async function persistTimeoutSessionRecord(args) {
10548
10687
  cumulativeDurationMs,
10549
10688
  cumulativeUsage,
10550
10689
  ...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
10690
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10551
10691
  sessionId: args.sessionId,
10552
10692
  sliceId: nextSliceId,
10553
10693
  state: "awaiting_resume",
@@ -10599,6 +10739,7 @@ async function persistYieldSessionRecord(args) {
10599
10739
  args.currentUsage
10600
10740
  ),
10601
10741
  ...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
10742
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10602
10743
  sessionId: args.sessionId,
10603
10744
  sliceId: args.currentSliceId,
10604
10745
  state: "awaiting_resume",
@@ -10815,6 +10956,7 @@ async function createMcpOAuthClientProvider(input) {
10815
10956
  userId: input.userId,
10816
10957
  conversationId: input.conversationId,
10817
10958
  ...input.destination ? { destination: input.destination } : {},
10959
+ ...input.source ? { source: input.source } : {},
10818
10960
  sessionId: input.sessionId,
10819
10961
  userMessage: input.userMessage,
10820
10962
  ...input.channelId ? { channelId: input.channelId } : {},
@@ -10916,6 +11058,7 @@ function createMcpAuthOrchestration(input) {
10916
11058
  provider: plugin.manifest.name,
10917
11059
  conversationId: input.conversationId,
10918
11060
  destination: input.destination,
11061
+ source: input.source,
10919
11062
  sessionId: input.sessionId,
10920
11063
  userId: input.requesterId,
10921
11064
  userMessage: input.userMessage,
@@ -11021,7 +11164,7 @@ function createMcpAuthOrchestration(input) {
11021
11164
 
11022
11165
  // src/chat/respond.ts
11023
11166
  var AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
11024
- function sleep2(ms) {
11167
+ function sleep3(ms) {
11025
11168
  return new Promise((resolve) => setTimeout(resolve, ms));
11026
11169
  }
11027
11170
  function waitForAbortSettlement(promise, timeoutMs) {
@@ -11078,8 +11221,7 @@ function extractSliceUsage(messages, beforeMessageCount) {
11078
11221
  return hasAgentTurnUsage(usage) ? usage : void 0;
11079
11222
  }
11080
11223
  function requesterFromContext(context) {
11081
- const identity = actorRequesterFromContext(context);
11082
- return identity?.platform === "slack" ? toStoredSlackRequester(identity) : void 0;
11224
+ return actorRequesterFromContext(context);
11083
11225
  }
11084
11226
  function assertRequesterDestinationMatch(context) {
11085
11227
  const { destination, requester } = context;
@@ -11113,23 +11255,11 @@ function assertCorrelationDestinationMatch(context) {
11113
11255
  }
11114
11256
  function actorRequesterFromContext(context) {
11115
11257
  return createRequester(context.requester, {
11116
- platform: context.requester?.platform ?? (context.destination?.platform === "slack" ? "slack" : void 0),
11117
- teamId: (context.destination?.platform === "slack" ? context.destination.teamId : void 0) ?? context.correlation?.teamId ?? (context.requester?.platform === "slack" ? context.requester.teamId : void 0),
11258
+ platform: context.requester?.platform ?? (context.destination.platform === "slack" ? "slack" : void 0),
11259
+ teamId: (context.destination.platform === "slack" ? context.destination.teamId : void 0) ?? context.correlation?.teamId ?? (context.requester?.platform === "slack" ? context.requester.teamId : void 0),
11118
11260
  userId: context.correlation?.requesterId
11119
11261
  });
11120
11262
  }
11121
- function toolInvocationSource(context) {
11122
- if (context.destination.platform !== "slack") {
11123
- return context.destination;
11124
- }
11125
- return {
11126
- platform: "slack",
11127
- teamId: context.destination.teamId,
11128
- channelId: context.destination.channelId,
11129
- ...context.correlation?.messageTs ? { messageTs: context.correlation.messageTs } : {},
11130
- ...context.correlation?.threadTs ? { threadTs: context.correlation.threadTs } : {}
11131
- };
11132
- }
11133
11263
  function toolInvocationDestination(context) {
11134
11264
  if (context.destination.platform !== "slack" || !context.toolChannelId) {
11135
11265
  return context.destination;
@@ -11148,10 +11278,6 @@ function surfaceFromContext(context) {
11148
11278
  if (context.slackConversation || (conversationId ? parseSlackThreadId(conversationId) : void 0)) {
11149
11279
  return "slack";
11150
11280
  }
11151
- const actor = context.credentialContext?.actor;
11152
- if (actor?.type === "system" && actor.id === "scheduler") {
11153
- return "scheduler";
11154
- }
11155
11281
  if (conversationId) {
11156
11282
  return "api";
11157
11283
  }
@@ -11245,7 +11371,20 @@ function buildSteeringPiMessage(message) {
11245
11371
  timestamp: message.timestampMs ?? Date.now()
11246
11372
  };
11247
11373
  }
11248
- async function generateAssistantReply(messageText, context) {
11374
+ function withoutTrailingUncheckpointedUserPrompt(messages, userContentParts) {
11375
+ if (!messages || messages.length === 0) {
11376
+ return [];
11377
+ }
11378
+ const lastMessage = messages.at(-1);
11379
+ if (lastMessage?.role !== "user") {
11380
+ return messages;
11381
+ }
11382
+ if (JSON.stringify(lastMessage.content) !== JSON.stringify(userContentParts)) {
11383
+ return messages;
11384
+ }
11385
+ return messages.slice(0, -1);
11386
+ }
11387
+ async function generateAssistantReply(messageText2, context) {
11249
11388
  if (!context.destination) {
11250
11389
  throw new TypeError("Assistant reply generation requires a destination");
11251
11390
  }
@@ -11277,6 +11416,7 @@ async function generateAssistantReply(messageText, context) {
11277
11416
  const requester = requesterFromContext(context);
11278
11417
  const actorRequester = actorRequesterFromContext(context);
11279
11418
  const surface = surfaceFromContext(context);
11419
+ const runSource = context.source;
11280
11420
  const credentialActor = context.credentialContext?.actor;
11281
11421
  const credentialActorLogContext = credentialActor ? {
11282
11422
  actorType: credentialActor.type,
@@ -11357,7 +11497,7 @@ async function generateAssistantReply(messageText, context) {
11357
11497
  }
11358
11498
  let baseInstructions = "";
11359
11499
  let configurationValues;
11360
- const userInput = messageText;
11500
+ const userInput = messageText2;
11361
11501
  if (shouldTrace) {
11362
11502
  const inboundAttachmentCount = context.inboundAttachmentCount ?? 0;
11363
11503
  const promptAttachmentCount = context.userAttachments?.length ?? 0;
@@ -11404,7 +11544,7 @@ async function generateAssistantReply(messageText, context) {
11404
11544
  };
11405
11545
  const authRequesterId = context.credentialContext?.actor.type === "user" ? context.credentialContext.actor.userId : void 0;
11406
11546
  const userTokenStore = createUserTokenStore();
11407
- const agentPluginHooks = createAgentPluginHookRunner({
11547
+ const pluginHooks = createPluginHookRunner({
11408
11548
  requester: actorRequester
11409
11549
  });
11410
11550
  sandboxExecutor = createSandboxExecutor({
@@ -11413,7 +11553,7 @@ async function generateAssistantReply(messageText, context) {
11413
11553
  traceContext: spanContext,
11414
11554
  tracePropagation: context.sandbox?.tracePropagation,
11415
11555
  credentialEgress: context.credentialContext,
11416
- agentHooks: agentPluginHooks,
11556
+ agentHooks: pluginHooks,
11417
11557
  onSandboxAcquired: async (sandbox2) => {
11418
11558
  lastKnownSandboxId = sandbox2.sandboxId;
11419
11559
  lastKnownSandboxDependencyProfileHash = sandbox2.sandboxDependencyProfileHash;
@@ -11514,14 +11654,7 @@ async function generateAssistantReply(messageText, context) {
11514
11654
  userAttachments: context.userAttachments,
11515
11655
  userTurnText
11516
11656
  });
11517
- const preAgentPromptMessages = () => existingSessionRecord?.piMessages ?? [
11518
- ...context.piMessages ?? [],
11519
- {
11520
- role: "user",
11521
- content: userContentParts,
11522
- timestamp: Date.now()
11523
- }
11524
- ];
11657
+ const preAgentPromptMessages = () => existingSessionRecord?.piMessages ?? [...context.piMessages ?? []];
11525
11658
  thinkingSelection = await selectTurnThinkingLevel({
11526
11659
  completeObject,
11527
11660
  conversationContext: context.conversationContext,
@@ -11564,6 +11697,7 @@ async function generateAssistantReply(messageText, context) {
11564
11697
  requesterId: authRequesterId,
11565
11698
  channelId: slackChannelId,
11566
11699
  destination: context.destination,
11700
+ source: runSource,
11567
11701
  threadTs: context.correlation?.threadTs,
11568
11702
  toolChannelId: context.toolChannelId,
11569
11703
  userMessage: userInput,
@@ -11581,6 +11715,7 @@ async function generateAssistantReply(messageText, context) {
11581
11715
  requesterId: authRequesterId,
11582
11716
  channelId: slackChannelId,
11583
11717
  destination: context.destination,
11718
+ source: runSource,
11584
11719
  threadTs: context.correlation?.threadTs,
11585
11720
  userMessage: userInput,
11586
11721
  channelConfiguration: context.channelConfiguration,
@@ -11615,6 +11750,7 @@ async function generateAssistantReply(messageText, context) {
11615
11750
  configuration: configurationValues,
11616
11751
  mcpToolManager: turnMcpToolManager,
11617
11752
  sandbox,
11753
+ surface,
11618
11754
  advisor: {
11619
11755
  config: botConfig.advisor,
11620
11756
  conversationId: sessionConversationId,
@@ -11624,20 +11760,26 @@ async function generateAssistantReply(messageText, context) {
11624
11760
  streamFn: createTracedStreamFn({ conversationPrivacy })
11625
11761
  }
11626
11762
  };
11627
- const toolSource = toolInvocationSource(context);
11763
+ const toolSource = runSource;
11628
11764
  const toolDestination = toolInvocationDestination(context);
11629
11765
  let toolRuntimeContext;
11630
11766
  if (toolSource.platform === "slack") {
11767
+ if (toolDestination.platform !== "slack") {
11768
+ throw new TypeError("Slack tool runtime requires a Slack destination");
11769
+ }
11631
11770
  toolRuntimeContext = {
11632
11771
  ...commonToolRuntimeContext,
11633
- ...toolDestination.platform === "slack" ? { destination: toolDestination } : {},
11772
+ destination: toolDestination,
11634
11773
  requester: actorRequester?.platform === "slack" ? actorRequester : void 0,
11635
11774
  source: toolSource
11636
11775
  };
11637
11776
  } else {
11777
+ if (toolDestination.platform !== "local") {
11778
+ throw new TypeError("Local tool runtime requires a local destination");
11779
+ }
11638
11780
  toolRuntimeContext = {
11639
11781
  ...commonToolRuntimeContext,
11640
- ...toolDestination.platform === "local" ? { destination: toolDestination } : {},
11782
+ destination: toolDestination,
11641
11783
  requester: actorRequester?.platform === "local" ? actorRequester : void 0,
11642
11784
  source: toolSource
11643
11785
  };
@@ -11722,17 +11864,39 @@ async function generateAssistantReply(messageText, context) {
11722
11864
  const activeMcpCatalogs = toActiveMcpCatalogSummaries(
11723
11865
  turnMcpToolManager.getActiveToolCatalog()
11724
11866
  );
11725
- baseInstructions = buildSystemPrompt({ source: toolSource });
11726
- const needsBootstrapContext = !hasRuntimeTurnContext(priorPiMessages ?? []);
11727
- const turnContextPrompt = needsBootstrapContext ? buildTurnContextPrompt({
11867
+ const hasPromptCheckpoint = resumedFromSessionRecord && existingSessionRecord?.turnStartMessageIndex !== void 0;
11868
+ const shouldPromptAgent = !resumedFromSessionRecord || !hasPromptCheckpoint;
11869
+ const promptHistoryMessages = shouldPromptAgent && resumedFromSessionRecord ? withoutTrailingUncheckpointedUserPrompt(
11870
+ priorPiMessages,
11871
+ userContentParts
11872
+ ) : shouldPromptAgent ? priorPiMessages ?? [] : existingSessionRecord.piMessages;
11873
+ const needsBootstrapContextForPrompt = shouldPromptAgent && !hasRuntimeTurnContext(promptHistoryMessages);
11874
+ const systemPromptContributions = await getPluginSystemPromptContributions(toolSource);
11875
+ const pluginSystemPrompt = buildPluginSystemPromptContributions(
11876
+ systemPromptContributions
11877
+ );
11878
+ baseInstructions = [
11879
+ buildSystemPrompt({ source: toolSource }),
11880
+ pluginSystemPrompt
11881
+ ].filter((section) => Boolean(section)).join("\n\n");
11882
+ const pluginUserPromptContributions = !shouldPromptAgent ? [] : await getPluginUserPromptContributions({
11883
+ context: toolRuntimeContext
11884
+ });
11885
+ const turnContextPrompt = needsBootstrapContextForPrompt || pluginUserPromptContributions.length > 0 ? buildTurnContextPrompt({
11728
11886
  availableSkills,
11729
11887
  activeMcpCatalogs,
11730
- includeSessionContext: true,
11888
+ includeSessionContext: needsBootstrapContextForPrompt,
11889
+ pluginPromptContributions: pluginUserPromptContributions,
11731
11890
  toolGuidance,
11732
11891
  runtime: {
11733
11892
  conversationId: spanContext.conversationId,
11734
11893
  slackConversation: context.slackConversation
11735
11894
  },
11895
+ dispatch: context.dispatch ? {
11896
+ ...context.dispatch,
11897
+ destination: context.destination,
11898
+ source: toolSource
11899
+ } : void 0,
11736
11900
  invocation: skillInvocation,
11737
11901
  requester: actorRequester,
11738
11902
  artifactState: context.artifactState,
@@ -11756,10 +11920,10 @@ async function generateAssistantReply(messageText, context) {
11756
11920
  const inputMessagesAttribute = serializeGenAiAttribute(
11757
11921
  conversationPrivacy !== "public" ? inputMessages.map(toGenAiMessageMetadata) : inputMessages
11758
11922
  );
11759
- const onToolCall = (toolName, params) => {
11923
+ const onToolCall = async (toolName, params) => {
11760
11924
  toolCalls.push(toolName);
11761
11925
  try {
11762
- context.onToolInvocation?.({ toolName, params });
11926
+ await context.onToolInvocation?.({ toolName, params });
11763
11927
  } catch (error) {
11764
11928
  logWarn(
11765
11929
  "tool_invocation_observer_failed",
@@ -11780,8 +11944,9 @@ async function generateAssistantReply(messageText, context) {
11780
11944
  sandboxExecutor,
11781
11945
  pluginAuth,
11782
11946
  onToolCall,
11783
- agentPluginHooks,
11784
- conversationPrivacy
11947
+ pluginHooks,
11948
+ conversationPrivacy,
11949
+ context.onToolResult
11785
11950
  );
11786
11951
  advisorTools = createAgentTools(
11787
11952
  createAdvisorToolDefinitions(tools),
@@ -11791,8 +11956,9 @@ async function generateAssistantReply(messageText, context) {
11791
11956
  sandboxExecutor,
11792
11957
  pluginAuth,
11793
11958
  onToolCall,
11794
- agentPluginHooks,
11795
- conversationPrivacy
11959
+ pluginHooks,
11960
+ conversationPrivacy,
11961
+ context.onToolResult
11796
11962
  );
11797
11963
  let hasEmittedText = false;
11798
11964
  let needsSeparator = false;
@@ -11811,6 +11977,7 @@ async function generateAssistantReply(messageText, context) {
11811
11977
  channelName: context.correlation?.channelName,
11812
11978
  conversationId: sessionConversationId,
11813
11979
  destination: context.destination,
11980
+ source: runSource,
11814
11981
  sessionId,
11815
11982
  sliceId: currentSliceId,
11816
11983
  messages,
@@ -11889,8 +12056,9 @@ async function generateAssistantReply(messageText, context) {
11889
12056
  );
11890
12057
  throw cooperativeYieldError;
11891
12058
  };
12059
+ const apiKeyOverride = getPiGatewayApiKey();
11892
12060
  agent = new Agent2({
11893
- getApiKey: () => getPiGatewayApiKeyOverride(),
12061
+ ...apiKeyOverride ? { getApiKey: () => apiKeyOverride } : {},
11894
12062
  streamFn: createTracedStreamFn({ conversationPrivacy }),
11895
12063
  steeringMode: "all",
11896
12064
  prepareNextTurn: async () => {
@@ -11949,16 +12117,13 @@ async function generateAssistantReply(messageText, context) {
11949
12117
  beforeMessageCount = agent.state.messages.length;
11950
12118
  try {
11951
12119
  if (resumedFromSessionRecord) {
11952
- agent.state.messages = turnContextPrompt ? prependMissingRuntimeTurnContext(
11953
- existingSessionRecord.piMessages,
11954
- turnContextPrompt
11955
- ) : existingSessionRecord.piMessages;
12120
+ agent.state.messages = shouldPromptAgent ? promptHistoryMessages ?? [] : existingSessionRecord.piMessages;
11956
12121
  turnStartMessageIndex = existingSessionRecord.turnStartMessageIndex;
11957
12122
  } else if (context.piMessages && context.piMessages.length > 0) {
11958
12123
  agent.state.messages = [...context.piMessages];
11959
12124
  }
11960
12125
  beforeMessageCount = agent.state.messages.length;
11961
- if (!resumedFromSessionRecord) {
12126
+ if (shouldPromptAgent) {
11962
12127
  turnStartMessageIndex = beforeMessageCount;
11963
12128
  }
11964
12129
  await withSpan(
@@ -11972,7 +12137,7 @@ async function generateAssistantReply(messageText, context) {
11972
12137
  content: promptContentParts,
11973
12138
  timestamp: Date.now()
11974
12139
  };
11975
- if (!resumedFromSessionRecord) {
12140
+ if (shouldPromptAgent) {
11976
12141
  const promptPersisted = await requireDurableInputCheckpoint([
11977
12142
  ...agent.state.messages,
11978
12143
  freshPromptMessage
@@ -12049,7 +12214,7 @@ async function generateAssistantReply(messageText, context) {
12049
12214
  }
12050
12215
  }
12051
12216
  };
12052
- let run = resumedFromSessionRecord ? agent.continue() : agent.prompt(freshPromptMessage);
12217
+ let run = shouldPromptAgent ? agent.prompt(freshPromptMessage) : agent.continue();
12053
12218
  let retryUsage;
12054
12219
  for (let attempt = 0; ; attempt += 1) {
12055
12220
  promptResult = await runAgentStep(run);
@@ -12098,7 +12263,7 @@ async function generateAssistantReply(messageText, context) {
12098
12263
  {},
12099
12264
  "Retrying transient provider failure"
12100
12265
  );
12101
- await sleep2(providerRetry.delayMs);
12266
+ await sleep3(providerRetry.delayMs);
12102
12267
  run = agent.continue();
12103
12268
  }
12104
12269
  },
@@ -12129,6 +12294,7 @@ async function generateAssistantReply(messageText, context) {
12129
12294
  currentDurationMs: Date.now() - replyStartedAtMs,
12130
12295
  currentUsage: turnUsage,
12131
12296
  destination: context.destination,
12297
+ source: runSource,
12132
12298
  sessionId,
12133
12299
  sliceId: currentSliceId,
12134
12300
  allMessages: agent.state.messages,
@@ -12165,6 +12331,7 @@ async function generateAssistantReply(messageText, context) {
12165
12331
  channelName: context.correlation?.channelName,
12166
12332
  conversationId: timeoutResumeConversationId,
12167
12333
  destination: context.destination,
12334
+ source: runSource,
12168
12335
  sessionId: timeoutResumeSessionId,
12169
12336
  currentSliceId: timeoutResumeSliceId,
12170
12337
  currentDurationMs: Date.now() - replyStartedAtMs,
@@ -12190,6 +12357,7 @@ async function generateAssistantReply(messageText, context) {
12190
12357
  channelName: context.correlation?.channelName,
12191
12358
  conversationId: timeoutResumeConversationId,
12192
12359
  destination: context.destination,
12360
+ source: runSource,
12193
12361
  sessionId: timeoutResumeSessionId,
12194
12362
  currentSliceId: timeoutResumeSliceId,
12195
12363
  currentDurationMs: Date.now() - replyStartedAtMs,
@@ -12234,6 +12402,7 @@ async function generateAssistantReply(messageText, context) {
12234
12402
  channelName: context.correlation?.channelName,
12235
12403
  conversationId: timeoutResumeConversationId,
12236
12404
  destination: context.destination,
12405
+ source: runSource,
12237
12406
  sessionId: timeoutResumeSessionId,
12238
12407
  currentSliceId: timeoutResumeSliceId,
12239
12408
  currentDurationMs: Date.now() - replyStartedAtMs,
@@ -12687,6 +12856,194 @@ function getConversationMessageSlackTs(message) {
12687
12856
  return message.meta?.slackTs ?? toOptionalString(message.id);
12688
12857
  }
12689
12858
 
12859
+ // src/chat/plugins/task-runner.ts
12860
+ import { pluginRunContextSchema } from "@sentry/junior-plugin-api";
12861
+ var PLUGIN_TASK_LOCK_TTL_MS = 5 * 60 * 1e3;
12862
+ function isRecord4(value) {
12863
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
12864
+ }
12865
+ function textPart(value) {
12866
+ if (isRecord4(value) && value.type === "text" && typeof value.text === "string") {
12867
+ return value.text;
12868
+ }
12869
+ return void 0;
12870
+ }
12871
+ function messageText(message) {
12872
+ const content = message.content;
12873
+ if (typeof content === "string") {
12874
+ return sanitizeText(content);
12875
+ }
12876
+ if (!Array.isArray(content)) {
12877
+ return "";
12878
+ }
12879
+ return sanitizeText(content.map(textPart).filter(Boolean).join("\n"));
12880
+ }
12881
+ function toolResultText(message) {
12882
+ const record = message;
12883
+ const parts = [
12884
+ messageText(message),
12885
+ record.output,
12886
+ record.result,
12887
+ record.stdout,
12888
+ record.stderr,
12889
+ record.toolResult
12890
+ ].filter(
12891
+ (value) => typeof value === "string" && value.length > 0
12892
+ );
12893
+ return sanitizeText(parts.join("\n"));
12894
+ }
12895
+ function sanitizeText(text) {
12896
+ return text.replace(
12897
+ /<data_base64>[\s\S]*?<\/data_base64>/g,
12898
+ "<data_base64>[omitted]</data_base64>"
12899
+ ).replace(
12900
+ /data:image\/[a-z0-9.+-]+;base64,[a-z0-9+/=]+/gi,
12901
+ "[image data omitted]"
12902
+ ).replaceAll("\0", " ").trim();
12903
+ }
12904
+ function runTranscriptEntry(message) {
12905
+ const role = getPiMessageRole(message);
12906
+ if (role === "user" || role === "assistant") {
12907
+ const text2 = messageText(message);
12908
+ if (!text2) {
12909
+ return void 0;
12910
+ }
12911
+ return { type: "message", role, text: text2 };
12912
+ }
12913
+ if (!isToolResultMessage(message)) {
12914
+ return void 0;
12915
+ }
12916
+ const toolName = normalizeToolNameFromResult(message);
12917
+ if (!toolName) {
12918
+ return void 0;
12919
+ }
12920
+ const text = toolResultText(message);
12921
+ return {
12922
+ type: "toolResult",
12923
+ toolName,
12924
+ isError: isToolResultError(message),
12925
+ ...text ? { text } : {}
12926
+ };
12927
+ }
12928
+ async function withPluginTaskLock(taskId, callback) {
12929
+ const state = getStateAdapter();
12930
+ await state.connect();
12931
+ const lock = await state.acquireLock(
12932
+ `plugin:task:${taskId}`,
12933
+ PLUGIN_TASK_LOCK_TTL_MS
12934
+ );
12935
+ if (!lock) {
12936
+ throw new Error(`Could not acquire plugin task lock for ${taskId}`);
12937
+ }
12938
+ try {
12939
+ return await callback();
12940
+ } finally {
12941
+ await state.releaseLock(lock);
12942
+ }
12943
+ }
12944
+ async function loadPluginRun(params) {
12945
+ const record = await getAgentTurnSessionRecord(
12946
+ params.conversationId,
12947
+ params.sessionId
12948
+ );
12949
+ if (!record) {
12950
+ throw new Error("Completed plugin task session record is unavailable");
12951
+ }
12952
+ if (record.state !== "completed") {
12953
+ throw new Error("Completed plugin task session record is not completed");
12954
+ }
12955
+ if (!record.source || !record.destination) {
12956
+ throw new Error(
12957
+ "Completed plugin task session record is missing source or destination"
12958
+ );
12959
+ }
12960
+ const sessionMessages = stripRuntimeTurnContext(
12961
+ record.piMessages.slice(record.turnStartMessageIndex ?? 0)
12962
+ );
12963
+ return pluginRunContextSchema.parse({
12964
+ completedAtMs: record.updatedAtMs,
12965
+ conversationId: record.conversationId,
12966
+ destination: record.destination,
12967
+ ...record.requester ? { requester: record.requester } : {},
12968
+ runId: record.sessionId,
12969
+ source: record.source,
12970
+ transcript: sessionMessages.map(runTranscriptEntry).filter((entry) => Boolean(entry))
12971
+ });
12972
+ }
12973
+ function taskPluginContext(plugin, message, options = {}) {
12974
+ const pluginName = plugin.manifest.name;
12975
+ const sessionParams = pluginTaskParamsSchema.parse(message.params);
12976
+ return {
12977
+ db: getDb(),
12978
+ embedder: createPluginEmbedder(pluginName, {
12979
+ signal: options.signal
12980
+ }),
12981
+ id: pluginTaskId(message),
12982
+ log: createPluginLogger(pluginName),
12983
+ model: createPluginModel(pluginName, plugin.model, {
12984
+ signal: options.signal
12985
+ }),
12986
+ name: message.name,
12987
+ plugin: { name: pluginName },
12988
+ run: {
12989
+ async load() {
12990
+ return await loadPluginRun(sessionParams);
12991
+ }
12992
+ },
12993
+ state: createPluginState(pluginName)
12994
+ };
12995
+ }
12996
+ function findPluginTask(message) {
12997
+ const plugin = getPlugins().find(
12998
+ (candidate) => candidate.manifest.name === message.plugin
12999
+ );
13000
+ if (!plugin?.tasks || !Object.hasOwn(plugin.tasks, message.name)) {
13001
+ return void 0;
13002
+ }
13003
+ const task = plugin.tasks[message.name];
13004
+ return { plugin, task };
13005
+ }
13006
+ async function scheduleSessionCompletedPluginTasks(params, options = {}) {
13007
+ const coreParams = pluginTaskParamsSchema.parse(params);
13008
+ const taskRegistrations = getPlugins().flatMap(
13009
+ (plugin) => Object.keys(plugin.tasks ?? {}).map((name) => ({ name, plugin }))
13010
+ );
13011
+ if (taskRegistrations.length === 0) {
13012
+ return;
13013
+ }
13014
+ const record = await getAgentTurnSessionRecord(
13015
+ coreParams.conversationId,
13016
+ coreParams.sessionId
13017
+ );
13018
+ if (!record || record.state !== "completed") {
13019
+ throw new Error("Completed plugin task session record is not ready");
13020
+ }
13021
+ const send = options.send ?? sendVercelPluginTask;
13022
+ const messages = taskRegistrations.map(({ name, plugin }) => ({
13023
+ name,
13024
+ params: coreParams,
13025
+ plugin: plugin.manifest.name
13026
+ }));
13027
+ await Promise.all(
13028
+ messages.map(async (message) => {
13029
+ await send(message);
13030
+ })
13031
+ );
13032
+ }
13033
+ async function processPluginTask(message, options = {}) {
13034
+ await withPluginTaskLock(pluginTaskId(message), async () => {
13035
+ const resolved = findPluginTask(message);
13036
+ if (!resolved) {
13037
+ throw new Error(
13038
+ `Plugin task "${message.plugin}.${message.name}" is not registered`
13039
+ );
13040
+ }
13041
+ await resolved.task.run(
13042
+ taskPluginContext(resolved.plugin, message, options)
13043
+ );
13044
+ });
13045
+ }
13046
+
12690
13047
  // src/chat/runtime/delivered-turn-state.ts
12691
13048
  function buildDeliveredTurnStatePatch(args) {
12692
13049
  const conversation = structuredClone(args.conversation);
@@ -12797,5 +13154,7 @@ export {
12797
13154
  createConversationMemoryService,
12798
13155
  isHumanConversationMessage,
12799
13156
  getConversationMessageSlackTs,
13157
+ scheduleSessionCompletedPluginTasks,
13158
+ processPluginTask,
12800
13159
  buildDeliveredTurnStatePatch
12801
13160
  };