@sentry/junior 0.75.0 → 0.76.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) 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 +2 -0
  5. package/dist/app.js +364 -135
  6. package/dist/build/virtual-config.d.ts +2 -2
  7. package/dist/chat/agent-dispatch/runner.d.ts +2 -0
  8. package/dist/chat/config.d.ts +1 -0
  9. package/dist/chat/credentials/state-adapter-token-store.d.ts +2 -0
  10. package/dist/chat/credentials/user-token-store.d.ts +17 -12
  11. package/dist/chat/db.d.ts +8 -0
  12. package/dist/chat/mcp/auth-store.d.ts +2 -1
  13. package/dist/chat/mcp/oauth.d.ts +2 -1
  14. package/dist/chat/oauth-flow.d.ts +3 -1
  15. package/dist/chat/pi/client.d.ts +15 -7
  16. package/dist/chat/plugins/agent-hooks.d.ts +7 -0
  17. package/dist/chat/plugins/auth/oauth-request.d.ts +11 -7
  18. package/dist/chat/plugins/model.d.ts +9 -0
  19. package/dist/chat/plugins/prompt.d.ts +5 -0
  20. package/dist/chat/plugins/task-callback.d.ts +5 -0
  21. package/dist/chat/plugins/task-message.d.ts +23 -0
  22. package/dist/chat/plugins/task-queue.d.ts +5 -0
  23. package/dist/chat/plugins/task-runner.d.ts +12 -0
  24. package/dist/chat/plugins/task-signing.d.ts +31 -0
  25. package/dist/chat/prompt.d.ts +4 -0
  26. package/dist/chat/requester.d.ts +6 -5
  27. package/dist/chat/respond-helpers.d.ts +2 -0
  28. package/dist/chat/respond.d.ts +4 -2
  29. package/dist/chat/runtime/agent-continue-runner.d.ts +4 -0
  30. package/dist/chat/runtime/reply-executor.d.ts +5 -1
  31. package/dist/chat/runtime/slack-resume.d.ts +10 -2
  32. package/dist/chat/sentry.d.ts +1 -0
  33. package/dist/chat/services/mcp-auth-orchestration.d.ts +2 -1
  34. package/dist/chat/services/plugin-auth-orchestration.d.ts +2 -1
  35. package/dist/chat/services/subscribed-decision.d.ts +2 -2
  36. package/dist/chat/services/turn-session-record.d.ts +11 -7
  37. package/dist/chat/slack/footer.d.ts +1 -1
  38. package/dist/chat/state/turn-session.d.ts +8 -5
  39. package/dist/chat/tools/agent-tools.d.ts +8 -1
  40. package/dist/chat/tools/slack/context.d.ts +2 -2
  41. package/dist/chat/tools/types.d.ts +4 -4
  42. package/dist/chat/vercel-queue-client.d.ts +3 -0
  43. package/dist/{chunk-C3AM4Z4J.js → chunk-2ECJXSVQ.js} +5 -5
  44. package/dist/{chunk-OJODNL2P.js → chunk-4SCWV7TJ.js} +2 -2
  45. package/dist/chunk-4UO6FK4G.js +64 -0
  46. package/dist/{chunk-BNJIEFQC.js → chunk-56TBVRJG.js} +2 -2
  47. package/dist/{chunk-OK4KKR7B.js → chunk-EJN6G5A2.js} +28 -12
  48. package/dist/{chunk-TQ74BATR.js → chunk-FFGXUXMD.js} +435 -111
  49. package/dist/{chunk-XJHDZUGD.js → chunk-JBASI5VV.js} +4 -4
  50. package/dist/chunk-KNFROR7R.js +127 -0
  51. package/dist/{chunk-VNTLUFTY.js → chunk-KOIMO7S3.js} +126 -87
  52. package/dist/chunk-MLKGABMK.js +9 -0
  53. package/dist/{chunk-NPVUAXUE.js → chunk-NFTMTIP3.js} +303 -33
  54. package/dist/chunk-NYKJ3KON.js +1082 -0
  55. package/dist/{chunk-SJHUF3DP.js → chunk-OJ53FYVG.js} +2 -10
  56. package/dist/{chunk-62FUNJYS.js → chunk-Q6XFTRV5.js} +54 -3
  57. package/dist/{chunk-UJ7OTHPO.js → chunk-R6Z5XWY3.js} +12 -670
  58. package/dist/chunk-RV5RYIJW.js +56 -0
  59. package/dist/{chunk-EE6PJWY4.js → chunk-SG5WAA7H.js} +7 -5
  60. package/dist/chunk-ST6YNAXG.js +54 -0
  61. package/dist/{chunk-FCZO7LAR.js → chunk-T77LUIX3.js} +139 -153
  62. package/dist/{chunk-EIYL7I4S.js → chunk-VALUBQ7R.js} +22 -30
  63. package/dist/{chunk-OZSPLAQ4.js → chunk-XBBC6W45.js} +1 -1
  64. package/dist/{chunk-ZNNTSPNF.js → chunk-Y5OFBCBZ.js} +1 -1
  65. package/dist/{chunk-74HO27II.js → chunk-Z4CIQ3EB.js} +5 -1
  66. package/dist/{chunk-2RWFUS5F.js → chunk-ZLMBNBUG.js} +101 -44
  67. package/dist/{chunk-JEELK46E.js → chunk-ZQB37HUX.js} +11 -11
  68. package/dist/cli/chat.js +52 -23
  69. package/dist/cli/check.js +7 -7
  70. package/dist/cli/env.js +4 -53
  71. package/dist/cli/init.js +6 -1
  72. package/dist/cli/main.js +84 -0
  73. package/dist/cli/plugins.js +244 -0
  74. package/dist/cli/run.js +5 -52
  75. package/dist/cli/snapshot-warmup.js +9 -9
  76. package/dist/cli/upgrade.js +167 -48
  77. package/dist/db-7A7PFRGL.js +17 -0
  78. package/dist/deployment.d.ts +1 -0
  79. package/dist/instrumentation.js +14 -18
  80. package/dist/nitro.d.ts +1 -1
  81. package/dist/nitro.js +43 -22
  82. package/dist/plugins-PZMDS7AT.js +15 -0
  83. package/dist/plugins.d.ts +4 -2
  84. package/dist/{registry-NLZFIW23.js → registry-OIPAJU2O.js} +6 -6
  85. package/dist/reporting.js +34 -26
  86. package/dist/{runner-LUQZ5G67.js → runner-7Z4D6AKV.js} +76 -23
  87. package/dist/sentry-4CP5NNQ5.js +31 -0
  88. package/dist/validation-SLA6IGF7.js +15 -0
  89. package/dist/vercel.js +1 -1
  90. package/package.json +8 -7
  91. package/dist/agent-hooks-2HEB4C3Q.js +0 -33
  92. package/dist/chat/conversations/configured.d.ts +0 -7
  93. package/dist/chat/conversations/state.d.ts +0 -4
  94. package/dist/chat/plugins/db.d.ts +0 -31
  95. package/dist/chunk-2KG3PWR4.js +0 -17
  96. package/dist/chunk-D7NFH5GD.js +0 -570
  97. package/dist/chunk-MCMROINU.js +0 -12
  98. package/dist/chunk-WBZ4M5N5.js +0 -59
  99. package/dist/db-A3ILH67H.js +0 -20
  100. package/dist/plugins-OMJKLRJ2.js +0 -13
  101. package/dist/validation-VMCPP3YO.js +0 -15
@@ -1,6 +1,25 @@
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
25
  escapeXml,
@@ -9,28 +28,35 @@ import {
9
28
  recordAuthorizationRequested,
10
29
  recordMcpProviderConnected,
11
30
  upsertAgentTurnSessionRecord
12
- } from "./chunk-VNTLUFTY.js";
31
+ } from "./chunk-KOIMO7S3.js";
13
32
  import {
33
+ createPluginEmbedder,
14
34
  createPluginHookRunner,
35
+ createPluginModel,
36
+ getPluginSystemPromptContributions,
15
37
  getPluginTools,
38
+ getPluginUserPromptContributions,
39
+ getPlugins,
16
40
  getSlackToolContext,
17
41
  resolveChannelCapabilities
18
- } from "./chunk-NPVUAXUE.js";
42
+ } from "./chunk-NFTMTIP3.js";
19
43
  import {
20
- discoverSkills,
21
- findSkillByName,
22
- loadSkillsByName,
23
- parseSkillInvocation
24
- } from "./chunk-OJODNL2P.js";
44
+ createPluginLogger,
45
+ createPluginState
46
+ } from "./chunk-56TBVRJG.js";
25
47
  import {
26
- buildConversationStatePatch
27
- } from "./chunk-74HO27II.js";
48
+ getDb
49
+ } from "./chunk-NYKJ3KON.js";
28
50
  import {
29
- JUNIOR_THREAD_STATE_TTL_MS
30
- } from "./chunk-UJ7OTHPO.js";
51
+ SANDBOX_DATA_ROOT,
52
+ SANDBOX_SKILLS_ROOT,
53
+ SANDBOX_WORKSPACE_ROOT,
54
+ sandboxSkillDir,
55
+ sandboxSkillFile
56
+ } from "./chunk-G3E7SCME.js";
31
57
  import {
32
- parseDestination
33
- } from "./chunk-WBZ4M5N5.js";
58
+ getStateAdapter
59
+ } from "./chunk-Y5OFBCBZ.js";
34
60
  import {
35
61
  SlackActionError,
36
62
  downloadPrivateSlackFile,
@@ -39,27 +65,9 @@ import {
39
65
  isConversationScopedChannel,
40
66
  isDmChannel,
41
67
  normalizeSlackConversationId,
68
+ parseDestination,
42
69
  withSlackRetries
43
- } from "./chunk-62FUNJYS.js";
44
- import {
45
- buildNonInteractiveShellScript,
46
- createSandboxInstance,
47
- getRuntimeDependencyProfileHash,
48
- getVercelSandboxCredentials,
49
- isSnapshotMissingError,
50
- resolveRuntimeDependencySnapshot,
51
- runNonInteractiveCommand
52
- } from "./chunk-XJHDZUGD.js";
53
- import {
54
- SANDBOX_DATA_ROOT,
55
- SANDBOX_SKILLS_ROOT,
56
- SANDBOX_WORKSPACE_ROOT,
57
- sandboxSkillDir,
58
- sandboxSkillFile
59
- } from "./chunk-G3E7SCME.js";
60
- import {
61
- getStateAdapter
62
- } from "./chunk-ZNNTSPNF.js";
70
+ } from "./chunk-Q6XFTRV5.js";
63
71
  import {
64
72
  GEN_AI_PROVIDER_NAME,
65
73
  GEN_AI_SERVER_ADDRESS,
@@ -72,7 +80,7 @@ import {
72
80
  encodeNonImageAttachmentForPrompt,
73
81
  extractAssistantText,
74
82
  getGatewayApiKey,
75
- getPiGatewayApiKeyOverride,
83
+ getPiGatewayApiKey,
76
84
  getPiMessageRole,
77
85
  getSessionIdentifiers,
78
86
  getTerminalAssistantMessages,
@@ -87,9 +95,9 @@ import {
87
95
  normalizeSlackEmojiName,
88
96
  normalizeToolNameFromResult,
89
97
  parseSlackThreadId,
90
- prependMissingRuntimeTurnContext,
91
98
  resolveConversationPrivacy,
92
99
  resolveGatewayModel,
100
+ stripRuntimeTurnContext,
93
101
  summarizeMessageText,
94
102
  toGenAiMessageMetadata,
95
103
  toGenAiMessagesTraceAttributes,
@@ -99,7 +107,13 @@ import {
99
107
  toObservablePromptPart,
100
108
  trimTrailingAssistantMessages,
101
109
  upsertActiveSkill
102
- } from "./chunk-FCZO7LAR.js";
110
+ } from "./chunk-T77LUIX3.js";
111
+ import {
112
+ discoverSkills,
113
+ findSkillByName,
114
+ loadSkillsByName,
115
+ parseSkillInvocation
116
+ } from "./chunk-4SCWV7TJ.js";
103
117
  import {
104
118
  createPluginBroker,
105
119
  credentialContextSchema,
@@ -113,15 +127,14 @@ import {
113
127
  isPluginConfigKey,
114
128
  resolveAuthTokenPlaceholder,
115
129
  resolvePluginCommandEnv
116
- } from "./chunk-2RWFUS5F.js";
130
+ } from "./chunk-ZLMBNBUG.js";
131
+ import {
132
+ createRequester,
133
+ parseActorUserId
134
+ } from "./chunk-VALUBQ7R.js";
117
135
  import {
118
136
  listReferenceFiles
119
137
  } from "./chunk-Q3XNY442.js";
120
- import {
121
- createRequester,
122
- parseActorUserId,
123
- toStoredSlackRequester
124
- } from "./chunk-EIYL7I4S.js";
125
138
  import {
126
139
  extractGenAiUsageAttributes,
127
140
  extractGenAiUsageSummary,
@@ -140,10 +153,11 @@ import {
140
153
  setTags,
141
154
  toOptionalString,
142
155
  withSpan
143
- } from "./chunk-OK4KKR7B.js";
156
+ } from "./chunk-EJN6G5A2.js";
144
157
  import {
145
- sentry_exports
146
- } from "./chunk-SJHUF3DP.js";
158
+ startInactiveSpan,
159
+ withActiveSpan
160
+ } from "./chunk-ST6YNAXG.js";
147
161
 
148
162
  // src/chat/configuration/defaults.ts
149
163
  var installDefaults = {};
@@ -728,31 +742,65 @@ var ProviderCredentialRouter = class {
728
742
  }
729
743
  };
730
744
 
745
+ // src/chat/credentials/user-token-store.ts
746
+ import {
747
+ pluginStoredTokensSchema
748
+ } from "@sentry/junior-plugin-api";
749
+ var storedTokensSchema = pluginStoredTokensSchema;
750
+
731
751
  // src/chat/credentials/state-adapter-token-store.ts
732
752
  var KEY_PREFIX = "oauth-token";
733
753
  var BUFFER_MS = 24 * 60 * 60 * 1e3;
734
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;
735
758
  function tokenKey(userId, provider) {
736
759
  return `${KEY_PREFIX}:${userId}:${provider}`;
737
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
+ }
738
767
  var StateAdapterTokenStore = class {
739
768
  state;
740
769
  constructor(stateAdapter) {
741
770
  this.state = stateAdapter;
742
771
  }
743
772
  async get(userId, provider) {
744
- const stored = await this.state.get(
745
- tokenKey(userId, provider)
746
- );
747
- 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);
748
775
  }
749
776
  async set(userId, provider, tokens) {
750
- const ttlMs = tokens.expiresAt ? Math.max(tokens.expiresAt - Date.now() + BUFFER_MS, BUFFER_MS) : LONG_LIVED_TTL_MS;
751
- 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);
752
781
  }
753
782
  async delete(userId, provider) {
754
783
  await this.state.delete(tokenKey(userId, provider));
755
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
+ }
756
804
  };
757
805
 
758
806
  // src/chat/capabilities/factory.ts
@@ -5908,8 +5956,9 @@ function createAdvisorTool(context) {
5908
5956
  "Advisor guidance is unavailable because advisor history could not be loaded. Continue without assuming advisor history."
5909
5957
  );
5910
5958
  }
5959
+ const apiKeyOverride = getPiGatewayApiKey();
5911
5960
  const advisorAgent = new Agent({
5912
- getApiKey: () => getPiGatewayApiKeyOverride(),
5961
+ ...apiKeyOverride ? { getApiKey: () => apiKeyOverride } : {},
5913
5962
  initialState: {
5914
5963
  systemPrompt: ADVISOR_SYSTEM_PROMPT,
5915
5964
  model: resolveGatewayModel(context.config.modelId),
@@ -6819,7 +6868,7 @@ function createTracedStreamFn(baseOrOptions = streamSimple) {
6819
6868
  const conversationPrivacy = typeof baseOrOptions === "function" ? void 0 : baseOrOptions.conversationPrivacy;
6820
6869
  const effectivePrivacy = conversationPrivacy ?? "private";
6821
6870
  return async (model, context, options) => {
6822
- const span = sentry_exports.startInactiveSpan({
6871
+ const span = startInactiveSpan({
6823
6872
  name: `chat ${model.id}`,
6824
6873
  op: "gen_ai.chat",
6825
6874
  attributes: {
@@ -6828,7 +6877,7 @@ function createTracedStreamFn(baseOrOptions = streamSimple) {
6828
6877
  }
6829
6878
  });
6830
6879
  try {
6831
- const stream = await sentry_exports.withActiveSpan(
6880
+ const stream = await withActiveSpan(
6832
6881
  span,
6833
6882
  () => Promise.resolve(base(model, context, options))
6834
6883
  );
@@ -6864,6 +6913,9 @@ import fs3 from "fs/promises";
6864
6913
 
6865
6914
  // src/chat/oauth-flow.ts
6866
6915
  import { randomBytes } from "crypto";
6916
+ import {
6917
+ sourceSchema
6918
+ } from "@sentry/junior-plugin-api";
6867
6919
  var OAUTH_STATE_TTL_MS = 10 * 60 * 1e3;
6868
6920
  function optionalString(value) {
6869
6921
  return typeof value === "string" ? value : void 0;
@@ -6879,13 +6931,22 @@ function parseOAuthStatePayload(value) {
6879
6931
  if (value.destination !== void 0 && !destination) {
6880
6932
  return void 0;
6881
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
+ }
6882
6942
  return {
6883
6943
  userId: value.userId,
6884
6944
  provider: value.provider,
6885
6945
  ...optionalString(value.channelId) ? { channelId: optionalString(value.channelId) } : {},
6886
6946
  ...destination ? { destination } : {},
6947
+ ...source?.success ? { source: source.data } : {},
6887
6948
  ...optionalString(value.threadTs) ? { threadTs: optionalString(value.threadTs) } : {},
6888
- ...optionalString(value.pendingMessage) ? { pendingMessage: optionalString(value.pendingMessage) } : {},
6949
+ ...pendingMessage ? { pendingMessage } : {},
6889
6950
  ...isRecord(value.configuration) ? { configuration: value.configuration } : {},
6890
6951
  ...optionalString(value.resumeConversationId) ? { resumeConversationId: optionalString(value.resumeConversationId) } : {},
6891
6952
  ...optionalString(value.resumeSessionId) ? { resumeSessionId: optionalString(value.resumeSessionId) } : {},
@@ -7007,6 +7068,7 @@ async function startOAuthFlow(provider, input) {
7007
7068
  provider,
7008
7069
  ...input.channelId ? { channelId: input.channelId } : {},
7009
7070
  ...input.destination ? { destination: input.destination } : {},
7071
+ ...input.source ? { source: input.source } : {},
7010
7072
  ...input.threadTs ? { threadTs: input.threadTs } : {},
7011
7073
  ...input.userMessage ? { pendingMessage: input.userMessage } : {},
7012
7074
  ...configuration && Object.keys(configuration).length > 0 ? { configuration } : {},
@@ -7843,7 +7905,7 @@ function truncateOutput(output, maxLength) {
7843
7905
  truncated: true
7844
7906
  };
7845
7907
  }
7846
- function sleep(ms) {
7908
+ function sleep2(ms) {
7847
7909
  return new Promise((resolve) => {
7848
7910
  setTimeout(resolve, ms);
7849
7911
  });
@@ -8014,7 +8076,7 @@ function createSandboxSessionManager(options) {
8014
8076
  if (!isSnapshottingError(error) || attempt === SNAPSHOT_BOOT_RETRY_COUNT - 1) {
8015
8077
  throw error;
8016
8078
  }
8017
- await sleep(SNAPSHOT_BOOT_RETRY_DELAY_MS);
8079
+ await sleep2(SNAPSHOT_BOOT_RETRY_DELAY_MS);
8018
8080
  }
8019
8081
  }
8020
8082
  throw new Error(`Failed to boot sandbox from snapshot ${snapshotId}`);
@@ -9141,6 +9203,9 @@ import { PluginToolInputError } from "@sentry/junior-plugin-api";
9141
9203
  import { THREAD_STATE_TTL_MS } from "chat";
9142
9204
 
9143
9205
  // src/chat/mcp/auth-store.ts
9206
+ import {
9207
+ sourceSchema as sourceSchema2
9208
+ } from "@sentry/junior-plugin-api";
9144
9209
  var MCP_AUTH_SESSION_PREFIX = "junior:mcp_auth_session";
9145
9210
  var MCP_AUTH_CREDENTIALS_PREFIX = "junior:mcp_auth_credentials";
9146
9211
  var MCP_AUTH_SESSION_INDEX_PREFIX = "junior:mcp_auth_session_index";
@@ -9192,12 +9257,17 @@ function parseMcpAuthSession(value) {
9192
9257
  if (parsed.destination !== void 0 && !destination) {
9193
9258
  return void 0;
9194
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
+ }
9195
9264
  return {
9196
9265
  authSessionId: parsed.authSessionId,
9197
9266
  provider: parsed.provider,
9198
9267
  userId: parsed.userId,
9199
9268
  conversationId: parsed.conversationId,
9200
9269
  ...destination ? { destination } : {},
9270
+ ...source?.success ? { source: source.data } : {},
9201
9271
  sessionId: parsed.sessionId,
9202
9272
  userMessage: parsed.userMessage,
9203
9273
  createdAtMs: parsed.createdAtMs,
@@ -9518,6 +9588,7 @@ function createPluginAuthOrchestration(input) {
9518
9588
  requesterId: input.requesterId,
9519
9589
  channelId: input.channelId,
9520
9590
  destination: input.destination,
9591
+ source: input.source,
9521
9592
  threadTs: input.threadTs,
9522
9593
  userMessage: input.userMessage,
9523
9594
  channelConfiguration: input.channelConfiguration,
@@ -9696,12 +9767,27 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, trac
9696
9767
  }
9697
9768
 
9698
9769
  // src/chat/tools/agent-tools.ts
9699
- function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor, pluginAuthOrchestration, onToolCall, agentHooks, conversationPrivacy) {
9770
+ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor, pluginAuthOrchestration, onToolCall, agentHooks, conversationPrivacy, onToolResult) {
9700
9771
  const shouldTrace = shouldEmitDevAgentTrace();
9701
9772
  const effectiveConversationPrivacy = conversationPrivacy ?? "private";
9702
9773
  const serializeToolPayload = (payload) => serializeGenAiAttribute(
9703
9774
  effectiveConversationPrivacy === "private" ? toGenAiPayloadMetadata(payload) : payload
9704
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
+ };
9705
9791
  return Object.entries(tools).map(([toolName, toolDef]) => ({
9706
9792
  name: toolName,
9707
9793
  label: toolName,
@@ -9751,7 +9837,7 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
9751
9837
  input: parsed
9752
9838
  }) : { input: parsed, env: {} };
9753
9839
  const toolInput = beforeTool.input;
9754
- onToolCall?.(toolName, toolInput);
9840
+ await onToolCall?.(toolName, toolInput);
9755
9841
  const sandboxInput = buildSandboxInput(toolName, toolInput);
9756
9842
  const isSandbox = Boolean(sandboxExecutor?.canExecute(toolName));
9757
9843
  const result = isSandbox ? await sandboxExecutor.execute({
@@ -9781,8 +9867,20 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
9781
9867
  )
9782
9868
  });
9783
9869
  }
9870
+ await notifyToolResult({
9871
+ ok: true,
9872
+ params: toolInput,
9873
+ result: resultAttributeValue,
9874
+ toolName
9875
+ });
9784
9876
  return normalized;
9785
9877
  } catch (error) {
9878
+ await notifyToolResult({
9879
+ error: error instanceof Error ? error.message : String(error),
9880
+ ok: false,
9881
+ params: parsed,
9882
+ toolName
9883
+ });
9786
9884
  if (error instanceof AuthorizationPauseError || error instanceof AuthorizationFlowDisabledError) {
9787
9885
  throw error;
9788
9886
  }
@@ -10389,7 +10487,7 @@ async function loadTurnSessionRecord(ctx) {
10389
10487
  const canUseTurnSession = Boolean(ctx.conversationId && ctx.sessionId);
10390
10488
  const existingSessionRecord = canUseTurnSession && ctx.conversationId && ctx.sessionId ? await getAgentTurnSessionRecord(ctx.conversationId, ctx.sessionId) : void 0;
10391
10489
  const hasAwaitingResumeRecord = Boolean(
10392
- existingSessionRecord && existingSessionRecord.state === "awaiting_resume" && existingSessionRecord.piMessages.length > 0
10490
+ existingSessionRecord && existingSessionRecord.state === "awaiting_resume"
10393
10491
  );
10394
10492
  return {
10395
10493
  canUseTurnSession,
@@ -10413,6 +10511,7 @@ async function persistRunningSessionRecord(args) {
10413
10511
  cumulativeDurationMs: latestSessionRecord?.cumulativeDurationMs,
10414
10512
  cumulativeUsage: latestSessionRecord?.cumulativeUsage,
10415
10513
  ...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
10514
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10416
10515
  sessionId: args.sessionId,
10417
10516
  sliceId: args.sliceId,
10418
10517
  state: "running",
@@ -10457,6 +10556,7 @@ async function persistCompletedSessionRecord(args) {
10457
10556
  args.currentUsage
10458
10557
  ),
10459
10558
  ...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
10559
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10460
10560
  sessionId: args.sessionId,
10461
10561
  sliceId: args.sliceId,
10462
10562
  state: "completed",
@@ -10492,7 +10592,7 @@ async function persistAuthPauseSessionRecord(args) {
10492
10592
  args.messages,
10493
10593
  latestSessionRecord?.piMessages
10494
10594
  );
10495
- if (piMessages.length === 0 || !isContinuableBoundary(piMessages)) {
10595
+ if (piMessages.length > 0 && !isContinuableBoundary(piMessages)) {
10496
10596
  return void 0;
10497
10597
  }
10498
10598
  return await upsertAgentTurnSessionRecord({
@@ -10507,6 +10607,7 @@ async function persistAuthPauseSessionRecord(args) {
10507
10607
  args.currentUsage
10508
10608
  ),
10509
10609
  ...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
10610
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10510
10611
  sessionId: args.sessionId,
10511
10612
  sliceId: nextSliceId,
10512
10613
  state: "awaiting_resume",
@@ -10566,6 +10667,7 @@ async function persistTimeoutSessionRecord(args) {
10566
10667
  ...args.destination ?? latestSessionRecord?.destination ? {
10567
10668
  destination: args.destination ?? latestSessionRecord?.destination
10568
10669
  } : {},
10670
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10569
10671
  sessionId: args.sessionId,
10570
10672
  sliceId: args.currentSliceId,
10571
10673
  state: "failed",
@@ -10585,6 +10687,7 @@ async function persistTimeoutSessionRecord(args) {
10585
10687
  cumulativeDurationMs,
10586
10688
  cumulativeUsage,
10587
10689
  ...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
10690
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10588
10691
  sessionId: args.sessionId,
10589
10692
  sliceId: nextSliceId,
10590
10693
  state: "awaiting_resume",
@@ -10636,6 +10739,7 @@ async function persistYieldSessionRecord(args) {
10636
10739
  args.currentUsage
10637
10740
  ),
10638
10741
  ...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
10742
+ ...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
10639
10743
  sessionId: args.sessionId,
10640
10744
  sliceId: args.currentSliceId,
10641
10745
  state: "awaiting_resume",
@@ -10852,6 +10956,7 @@ async function createMcpOAuthClientProvider(input) {
10852
10956
  userId: input.userId,
10853
10957
  conversationId: input.conversationId,
10854
10958
  ...input.destination ? { destination: input.destination } : {},
10959
+ ...input.source ? { source: input.source } : {},
10855
10960
  sessionId: input.sessionId,
10856
10961
  userMessage: input.userMessage,
10857
10962
  ...input.channelId ? { channelId: input.channelId } : {},
@@ -10953,6 +11058,7 @@ function createMcpAuthOrchestration(input) {
10953
11058
  provider: plugin.manifest.name,
10954
11059
  conversationId: input.conversationId,
10955
11060
  destination: input.destination,
11061
+ source: input.source,
10956
11062
  sessionId: input.sessionId,
10957
11063
  userId: input.requesterId,
10958
11064
  userMessage: input.userMessage,
@@ -11058,7 +11164,7 @@ function createMcpAuthOrchestration(input) {
11058
11164
 
11059
11165
  // src/chat/respond.ts
11060
11166
  var AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
11061
- function sleep2(ms) {
11167
+ function sleep3(ms) {
11062
11168
  return new Promise((resolve) => setTimeout(resolve, ms));
11063
11169
  }
11064
11170
  function waitForAbortSettlement(promise, timeoutMs) {
@@ -11115,8 +11221,7 @@ function extractSliceUsage(messages, beforeMessageCount) {
11115
11221
  return hasAgentTurnUsage(usage) ? usage : void 0;
11116
11222
  }
11117
11223
  function requesterFromContext(context) {
11118
- const identity = actorRequesterFromContext(context);
11119
- return identity?.platform === "slack" ? toStoredSlackRequester(identity) : void 0;
11224
+ return actorRequesterFromContext(context);
11120
11225
  }
11121
11226
  function assertRequesterDestinationMatch(context) {
11122
11227
  const { destination, requester } = context;
@@ -11150,26 +11255,11 @@ function assertCorrelationDestinationMatch(context) {
11150
11255
  }
11151
11256
  function actorRequesterFromContext(context) {
11152
11257
  return createRequester(context.requester, {
11153
- platform: context.requester?.platform ?? (context.destination?.platform === "slack" ? "slack" : void 0),
11154
- 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),
11155
11260
  userId: context.correlation?.requesterId
11156
11261
  });
11157
11262
  }
11158
- function toolInvocationSource(context) {
11159
- if (context.source) {
11160
- return context.source;
11161
- }
11162
- if (context.destination.platform !== "slack") {
11163
- return context.destination;
11164
- }
11165
- return {
11166
- platform: "slack",
11167
- teamId: context.destination.teamId,
11168
- channelId: context.destination.channelId,
11169
- ...context.correlation?.messageTs ? { messageTs: context.correlation.messageTs } : {},
11170
- ...context.correlation?.threadTs ? { threadTs: context.correlation.threadTs } : {}
11171
- };
11172
- }
11173
11263
  function toolInvocationDestination(context) {
11174
11264
  if (context.destination.platform !== "slack" || !context.toolChannelId) {
11175
11265
  return context.destination;
@@ -11281,7 +11371,20 @@ function buildSteeringPiMessage(message) {
11281
11371
  timestamp: message.timestampMs ?? Date.now()
11282
11372
  };
11283
11373
  }
11284
- 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) {
11285
11388
  if (!context.destination) {
11286
11389
  throw new TypeError("Assistant reply generation requires a destination");
11287
11390
  }
@@ -11313,6 +11416,7 @@ async function generateAssistantReply(messageText, context) {
11313
11416
  const requester = requesterFromContext(context);
11314
11417
  const actorRequester = actorRequesterFromContext(context);
11315
11418
  const surface = surfaceFromContext(context);
11419
+ const runSource = context.source;
11316
11420
  const credentialActor = context.credentialContext?.actor;
11317
11421
  const credentialActorLogContext = credentialActor ? {
11318
11422
  actorType: credentialActor.type,
@@ -11393,7 +11497,7 @@ async function generateAssistantReply(messageText, context) {
11393
11497
  }
11394
11498
  let baseInstructions = "";
11395
11499
  let configurationValues;
11396
- const userInput = messageText;
11500
+ const userInput = messageText2;
11397
11501
  if (shouldTrace) {
11398
11502
  const inboundAttachmentCount = context.inboundAttachmentCount ?? 0;
11399
11503
  const promptAttachmentCount = context.userAttachments?.length ?? 0;
@@ -11550,14 +11654,7 @@ async function generateAssistantReply(messageText, context) {
11550
11654
  userAttachments: context.userAttachments,
11551
11655
  userTurnText
11552
11656
  });
11553
- const preAgentPromptMessages = () => existingSessionRecord?.piMessages ?? [
11554
- ...context.piMessages ?? [],
11555
- {
11556
- role: "user",
11557
- content: userContentParts,
11558
- timestamp: Date.now()
11559
- }
11560
- ];
11657
+ const preAgentPromptMessages = () => existingSessionRecord?.piMessages ?? [...context.piMessages ?? []];
11561
11658
  thinkingSelection = await selectTurnThinkingLevel({
11562
11659
  completeObject,
11563
11660
  conversationContext: context.conversationContext,
@@ -11600,6 +11697,7 @@ async function generateAssistantReply(messageText, context) {
11600
11697
  requesterId: authRequesterId,
11601
11698
  channelId: slackChannelId,
11602
11699
  destination: context.destination,
11700
+ source: runSource,
11603
11701
  threadTs: context.correlation?.threadTs,
11604
11702
  toolChannelId: context.toolChannelId,
11605
11703
  userMessage: userInput,
@@ -11617,6 +11715,7 @@ async function generateAssistantReply(messageText, context) {
11617
11715
  requesterId: authRequesterId,
11618
11716
  channelId: slackChannelId,
11619
11717
  destination: context.destination,
11718
+ source: runSource,
11620
11719
  threadTs: context.correlation?.threadTs,
11621
11720
  userMessage: userInput,
11622
11721
  channelConfiguration: context.channelConfiguration,
@@ -11661,20 +11760,26 @@ async function generateAssistantReply(messageText, context) {
11661
11760
  streamFn: createTracedStreamFn({ conversationPrivacy })
11662
11761
  }
11663
11762
  };
11664
- const toolSource = toolInvocationSource(context);
11763
+ const toolSource = runSource;
11665
11764
  const toolDestination = toolInvocationDestination(context);
11666
11765
  let toolRuntimeContext;
11667
11766
  if (toolSource.platform === "slack") {
11767
+ if (toolDestination.platform !== "slack") {
11768
+ throw new TypeError("Slack tool runtime requires a Slack destination");
11769
+ }
11668
11770
  toolRuntimeContext = {
11669
11771
  ...commonToolRuntimeContext,
11670
- ...toolDestination.platform === "slack" ? { destination: toolDestination } : {},
11772
+ destination: toolDestination,
11671
11773
  requester: actorRequester?.platform === "slack" ? actorRequester : void 0,
11672
11774
  source: toolSource
11673
11775
  };
11674
11776
  } else {
11777
+ if (toolDestination.platform !== "local") {
11778
+ throw new TypeError("Local tool runtime requires a local destination");
11779
+ }
11675
11780
  toolRuntimeContext = {
11676
11781
  ...commonToolRuntimeContext,
11677
- ...toolDestination.platform === "local" ? { destination: toolDestination } : {},
11782
+ destination: toolDestination,
11678
11783
  requester: actorRequester?.platform === "local" ? actorRequester : void 0,
11679
11784
  source: toolSource
11680
11785
  };
@@ -11734,11 +11839,15 @@ async function generateAssistantReply(messageText, context) {
11734
11839
  promptGuidelines: definition.promptGuidelines,
11735
11840
  promptSnippet: definition.promptSnippet
11736
11841
  }));
11842
+ const pendingMcpProvider = context.pendingAuth?.kind === "mcp" ? context.pendingAuth.provider : void 0;
11737
11843
  const providersToRestore = /* @__PURE__ */ new Set([
11738
11844
  ...connectedMcpProviders,
11739
11845
  ...inferActiveMcpProvidersFromPiMessages(priorPiMessages)
11740
11846
  ]);
11741
11847
  for (const provider of providersToRestore) {
11848
+ if (provider === pendingMcpProvider) {
11849
+ continue;
11850
+ }
11742
11851
  if (await turnMcpToolManager.activateProvider(provider)) {
11743
11852
  await recordConnectedMcpProvider(provider);
11744
11853
  }
@@ -11748,6 +11857,9 @@ async function generateAssistantReply(messageText, context) {
11748
11857
  }
11749
11858
  }
11750
11859
  for (const skill of activeSkills) {
11860
+ if (skill.pluginProvider === pendingMcpProvider) {
11861
+ continue;
11862
+ }
11751
11863
  if (await turnMcpToolManager.activateForSkill(skill)) {
11752
11864
  await recordConnectedMcpProvider(skill.pluginProvider);
11753
11865
  }
@@ -11759,12 +11871,29 @@ async function generateAssistantReply(messageText, context) {
11759
11871
  const activeMcpCatalogs = toActiveMcpCatalogSummaries(
11760
11872
  turnMcpToolManager.getActiveToolCatalog()
11761
11873
  );
11762
- baseInstructions = buildSystemPrompt({ source: toolSource });
11763
- const needsBootstrapContext = !hasRuntimeTurnContext(priorPiMessages ?? []);
11764
- const turnContextPrompt = needsBootstrapContext ? buildTurnContextPrompt({
11874
+ const hasPromptCheckpoint = resumedFromSessionRecord && existingSessionRecord?.turnStartMessageIndex !== void 0;
11875
+ const shouldPromptAgent = !resumedFromSessionRecord || !hasPromptCheckpoint;
11876
+ const promptHistoryMessages = shouldPromptAgent && resumedFromSessionRecord ? withoutTrailingUncheckpointedUserPrompt(
11877
+ priorPiMessages,
11878
+ userContentParts
11879
+ ) : shouldPromptAgent ? priorPiMessages ?? [] : existingSessionRecord.piMessages;
11880
+ const needsBootstrapContextForPrompt = shouldPromptAgent && !hasRuntimeTurnContext(promptHistoryMessages);
11881
+ const systemPromptContributions = await getPluginSystemPromptContributions(toolSource);
11882
+ const pluginSystemPrompt = buildPluginSystemPromptContributions(
11883
+ systemPromptContributions
11884
+ );
11885
+ baseInstructions = [
11886
+ buildSystemPrompt({ source: toolSource }),
11887
+ pluginSystemPrompt
11888
+ ].filter((section) => Boolean(section)).join("\n\n");
11889
+ const pluginUserPromptContributions = !shouldPromptAgent ? [] : await getPluginUserPromptContributions({
11890
+ context: toolRuntimeContext
11891
+ });
11892
+ const turnContextPrompt = needsBootstrapContextForPrompt || pluginUserPromptContributions.length > 0 ? buildTurnContextPrompt({
11765
11893
  availableSkills,
11766
11894
  activeMcpCatalogs,
11767
- includeSessionContext: true,
11895
+ includeSessionContext: needsBootstrapContextForPrompt,
11896
+ pluginPromptContributions: pluginUserPromptContributions,
11768
11897
  toolGuidance,
11769
11898
  runtime: {
11770
11899
  conversationId: spanContext.conversationId,
@@ -11798,10 +11927,10 @@ async function generateAssistantReply(messageText, context) {
11798
11927
  const inputMessagesAttribute = serializeGenAiAttribute(
11799
11928
  conversationPrivacy !== "public" ? inputMessages.map(toGenAiMessageMetadata) : inputMessages
11800
11929
  );
11801
- const onToolCall = (toolName, params) => {
11930
+ const onToolCall = async (toolName, params) => {
11802
11931
  toolCalls.push(toolName);
11803
11932
  try {
11804
- context.onToolInvocation?.({ toolName, params });
11933
+ await context.onToolInvocation?.({ toolName, params });
11805
11934
  } catch (error) {
11806
11935
  logWarn(
11807
11936
  "tool_invocation_observer_failed",
@@ -11823,7 +11952,8 @@ async function generateAssistantReply(messageText, context) {
11823
11952
  pluginAuth,
11824
11953
  onToolCall,
11825
11954
  pluginHooks,
11826
- conversationPrivacy
11955
+ conversationPrivacy,
11956
+ context.onToolResult
11827
11957
  );
11828
11958
  advisorTools = createAgentTools(
11829
11959
  createAdvisorToolDefinitions(tools),
@@ -11834,7 +11964,8 @@ async function generateAssistantReply(messageText, context) {
11834
11964
  pluginAuth,
11835
11965
  onToolCall,
11836
11966
  pluginHooks,
11837
- conversationPrivacy
11967
+ conversationPrivacy,
11968
+ context.onToolResult
11838
11969
  );
11839
11970
  let hasEmittedText = false;
11840
11971
  let needsSeparator = false;
@@ -11853,6 +11984,7 @@ async function generateAssistantReply(messageText, context) {
11853
11984
  channelName: context.correlation?.channelName,
11854
11985
  conversationId: sessionConversationId,
11855
11986
  destination: context.destination,
11987
+ source: runSource,
11856
11988
  sessionId,
11857
11989
  sliceId: currentSliceId,
11858
11990
  messages,
@@ -11931,8 +12063,9 @@ async function generateAssistantReply(messageText, context) {
11931
12063
  );
11932
12064
  throw cooperativeYieldError;
11933
12065
  };
12066
+ const apiKeyOverride = getPiGatewayApiKey();
11934
12067
  agent = new Agent2({
11935
- getApiKey: () => getPiGatewayApiKeyOverride(),
12068
+ ...apiKeyOverride ? { getApiKey: () => apiKeyOverride } : {},
11936
12069
  streamFn: createTracedStreamFn({ conversationPrivacy }),
11937
12070
  steeringMode: "all",
11938
12071
  prepareNextTurn: async () => {
@@ -11991,16 +12124,13 @@ async function generateAssistantReply(messageText, context) {
11991
12124
  beforeMessageCount = agent.state.messages.length;
11992
12125
  try {
11993
12126
  if (resumedFromSessionRecord) {
11994
- agent.state.messages = turnContextPrompt ? prependMissingRuntimeTurnContext(
11995
- existingSessionRecord.piMessages,
11996
- turnContextPrompt
11997
- ) : existingSessionRecord.piMessages;
12127
+ agent.state.messages = shouldPromptAgent ? promptHistoryMessages ?? [] : existingSessionRecord.piMessages;
11998
12128
  turnStartMessageIndex = existingSessionRecord.turnStartMessageIndex;
11999
12129
  } else if (context.piMessages && context.piMessages.length > 0) {
12000
12130
  agent.state.messages = [...context.piMessages];
12001
12131
  }
12002
12132
  beforeMessageCount = agent.state.messages.length;
12003
- if (!resumedFromSessionRecord) {
12133
+ if (shouldPromptAgent) {
12004
12134
  turnStartMessageIndex = beforeMessageCount;
12005
12135
  }
12006
12136
  await withSpan(
@@ -12014,7 +12144,7 @@ async function generateAssistantReply(messageText, context) {
12014
12144
  content: promptContentParts,
12015
12145
  timestamp: Date.now()
12016
12146
  };
12017
- if (!resumedFromSessionRecord) {
12147
+ if (shouldPromptAgent) {
12018
12148
  const promptPersisted = await requireDurableInputCheckpoint([
12019
12149
  ...agent.state.messages,
12020
12150
  freshPromptMessage
@@ -12091,7 +12221,7 @@ async function generateAssistantReply(messageText, context) {
12091
12221
  }
12092
12222
  }
12093
12223
  };
12094
- let run = resumedFromSessionRecord ? agent.continue() : agent.prompt(freshPromptMessage);
12224
+ let run = shouldPromptAgent ? agent.prompt(freshPromptMessage) : agent.continue();
12095
12225
  let retryUsage;
12096
12226
  for (let attempt = 0; ; attempt += 1) {
12097
12227
  promptResult = await runAgentStep(run);
@@ -12140,7 +12270,7 @@ async function generateAssistantReply(messageText, context) {
12140
12270
  {},
12141
12271
  "Retrying transient provider failure"
12142
12272
  );
12143
- await sleep2(providerRetry.delayMs);
12273
+ await sleep3(providerRetry.delayMs);
12144
12274
  run = agent.continue();
12145
12275
  }
12146
12276
  },
@@ -12171,6 +12301,7 @@ async function generateAssistantReply(messageText, context) {
12171
12301
  currentDurationMs: Date.now() - replyStartedAtMs,
12172
12302
  currentUsage: turnUsage,
12173
12303
  destination: context.destination,
12304
+ source: runSource,
12174
12305
  sessionId,
12175
12306
  sliceId: currentSliceId,
12176
12307
  allMessages: agent.state.messages,
@@ -12207,6 +12338,7 @@ async function generateAssistantReply(messageText, context) {
12207
12338
  channelName: context.correlation?.channelName,
12208
12339
  conversationId: timeoutResumeConversationId,
12209
12340
  destination: context.destination,
12341
+ source: runSource,
12210
12342
  sessionId: timeoutResumeSessionId,
12211
12343
  currentSliceId: timeoutResumeSliceId,
12212
12344
  currentDurationMs: Date.now() - replyStartedAtMs,
@@ -12232,6 +12364,7 @@ async function generateAssistantReply(messageText, context) {
12232
12364
  channelName: context.correlation?.channelName,
12233
12365
  conversationId: timeoutResumeConversationId,
12234
12366
  destination: context.destination,
12367
+ source: runSource,
12235
12368
  sessionId: timeoutResumeSessionId,
12236
12369
  currentSliceId: timeoutResumeSliceId,
12237
12370
  currentDurationMs: Date.now() - replyStartedAtMs,
@@ -12276,6 +12409,7 @@ async function generateAssistantReply(messageText, context) {
12276
12409
  channelName: context.correlation?.channelName,
12277
12410
  conversationId: timeoutResumeConversationId,
12278
12411
  destination: context.destination,
12412
+ source: runSource,
12279
12413
  sessionId: timeoutResumeSessionId,
12280
12414
  currentSliceId: timeoutResumeSliceId,
12281
12415
  currentDurationMs: Date.now() - replyStartedAtMs,
@@ -12729,6 +12863,194 @@ function getConversationMessageSlackTs(message) {
12729
12863
  return message.meta?.slackTs ?? toOptionalString(message.id);
12730
12864
  }
12731
12865
 
12866
+ // src/chat/plugins/task-runner.ts
12867
+ import { pluginRunContextSchema } from "@sentry/junior-plugin-api";
12868
+ var PLUGIN_TASK_LOCK_TTL_MS = 5 * 60 * 1e3;
12869
+ function isRecord4(value) {
12870
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
12871
+ }
12872
+ function textPart(value) {
12873
+ if (isRecord4(value) && value.type === "text" && typeof value.text === "string") {
12874
+ return value.text;
12875
+ }
12876
+ return void 0;
12877
+ }
12878
+ function messageText(message) {
12879
+ const content = message.content;
12880
+ if (typeof content === "string") {
12881
+ return sanitizeText(content);
12882
+ }
12883
+ if (!Array.isArray(content)) {
12884
+ return "";
12885
+ }
12886
+ return sanitizeText(content.map(textPart).filter(Boolean).join("\n"));
12887
+ }
12888
+ function toolResultText(message) {
12889
+ const record = message;
12890
+ const parts = [
12891
+ messageText(message),
12892
+ record.output,
12893
+ record.result,
12894
+ record.stdout,
12895
+ record.stderr,
12896
+ record.toolResult
12897
+ ].filter(
12898
+ (value) => typeof value === "string" && value.length > 0
12899
+ );
12900
+ return sanitizeText(parts.join("\n"));
12901
+ }
12902
+ function sanitizeText(text) {
12903
+ return text.replace(
12904
+ /<data_base64>[\s\S]*?<\/data_base64>/g,
12905
+ "<data_base64>[omitted]</data_base64>"
12906
+ ).replace(
12907
+ /data:image\/[a-z0-9.+-]+;base64,[a-z0-9+/=]+/gi,
12908
+ "[image data omitted]"
12909
+ ).replaceAll("\0", " ").trim();
12910
+ }
12911
+ function runTranscriptEntry(message) {
12912
+ const role = getPiMessageRole(message);
12913
+ if (role === "user" || role === "assistant") {
12914
+ const text2 = messageText(message);
12915
+ if (!text2) {
12916
+ return void 0;
12917
+ }
12918
+ return { type: "message", role, text: text2 };
12919
+ }
12920
+ if (!isToolResultMessage(message)) {
12921
+ return void 0;
12922
+ }
12923
+ const toolName = normalizeToolNameFromResult(message);
12924
+ if (!toolName) {
12925
+ return void 0;
12926
+ }
12927
+ const text = toolResultText(message);
12928
+ return {
12929
+ type: "toolResult",
12930
+ toolName,
12931
+ isError: isToolResultError(message),
12932
+ ...text ? { text } : {}
12933
+ };
12934
+ }
12935
+ async function withPluginTaskLock(taskId, callback) {
12936
+ const state = getStateAdapter();
12937
+ await state.connect();
12938
+ const lock = await state.acquireLock(
12939
+ `plugin:task:${taskId}`,
12940
+ PLUGIN_TASK_LOCK_TTL_MS
12941
+ );
12942
+ if (!lock) {
12943
+ throw new Error(`Could not acquire plugin task lock for ${taskId}`);
12944
+ }
12945
+ try {
12946
+ return await callback();
12947
+ } finally {
12948
+ await state.releaseLock(lock);
12949
+ }
12950
+ }
12951
+ async function loadPluginRun(params) {
12952
+ const record = await getAgentTurnSessionRecord(
12953
+ params.conversationId,
12954
+ params.sessionId
12955
+ );
12956
+ if (!record) {
12957
+ throw new Error("Completed plugin task session record is unavailable");
12958
+ }
12959
+ if (record.state !== "completed") {
12960
+ throw new Error("Completed plugin task session record is not completed");
12961
+ }
12962
+ if (!record.source || !record.destination) {
12963
+ throw new Error(
12964
+ "Completed plugin task session record is missing source or destination"
12965
+ );
12966
+ }
12967
+ const sessionMessages = stripRuntimeTurnContext(
12968
+ record.piMessages.slice(record.turnStartMessageIndex ?? 0)
12969
+ );
12970
+ return pluginRunContextSchema.parse({
12971
+ completedAtMs: record.updatedAtMs,
12972
+ conversationId: record.conversationId,
12973
+ destination: record.destination,
12974
+ ...record.requester ? { requester: record.requester } : {},
12975
+ runId: record.sessionId,
12976
+ source: record.source,
12977
+ transcript: sessionMessages.map(runTranscriptEntry).filter((entry) => Boolean(entry))
12978
+ });
12979
+ }
12980
+ function taskPluginContext(plugin, message, options = {}) {
12981
+ const pluginName = plugin.manifest.name;
12982
+ const sessionParams = pluginTaskParamsSchema.parse(message.params);
12983
+ return {
12984
+ db: getDb(),
12985
+ embedder: createPluginEmbedder(pluginName, {
12986
+ signal: options.signal
12987
+ }),
12988
+ id: pluginTaskId(message),
12989
+ log: createPluginLogger(pluginName),
12990
+ model: createPluginModel(pluginName, plugin.model, {
12991
+ signal: options.signal
12992
+ }),
12993
+ name: message.name,
12994
+ plugin: { name: pluginName },
12995
+ run: {
12996
+ async load() {
12997
+ return await loadPluginRun(sessionParams);
12998
+ }
12999
+ },
13000
+ state: createPluginState(pluginName)
13001
+ };
13002
+ }
13003
+ function findPluginTask(message) {
13004
+ const plugin = getPlugins().find(
13005
+ (candidate) => candidate.manifest.name === message.plugin
13006
+ );
13007
+ if (!plugin?.tasks || !Object.hasOwn(plugin.tasks, message.name)) {
13008
+ return void 0;
13009
+ }
13010
+ const task = plugin.tasks[message.name];
13011
+ return { plugin, task };
13012
+ }
13013
+ async function scheduleSessionCompletedPluginTasks(params, options = {}) {
13014
+ const coreParams = pluginTaskParamsSchema.parse(params);
13015
+ const taskRegistrations = getPlugins().flatMap(
13016
+ (plugin) => Object.keys(plugin.tasks ?? {}).map((name) => ({ name, plugin }))
13017
+ );
13018
+ if (taskRegistrations.length === 0) {
13019
+ return;
13020
+ }
13021
+ const record = await getAgentTurnSessionRecord(
13022
+ coreParams.conversationId,
13023
+ coreParams.sessionId
13024
+ );
13025
+ if (!record || record.state !== "completed") {
13026
+ throw new Error("Completed plugin task session record is not ready");
13027
+ }
13028
+ const send = options.send ?? sendVercelPluginTask;
13029
+ const messages = taskRegistrations.map(({ name, plugin }) => ({
13030
+ name,
13031
+ params: coreParams,
13032
+ plugin: plugin.manifest.name
13033
+ }));
13034
+ await Promise.all(
13035
+ messages.map(async (message) => {
13036
+ await send(message);
13037
+ })
13038
+ );
13039
+ }
13040
+ async function processPluginTask(message, options = {}) {
13041
+ await withPluginTaskLock(pluginTaskId(message), async () => {
13042
+ const resolved = findPluginTask(message);
13043
+ if (!resolved) {
13044
+ throw new Error(
13045
+ `Plugin task "${message.plugin}.${message.name}" is not registered`
13046
+ );
13047
+ }
13048
+ await resolved.task.run(
13049
+ taskPluginContext(resolved.plugin, message, options)
13050
+ );
13051
+ });
13052
+ }
13053
+
12732
13054
  // src/chat/runtime/delivered-turn-state.ts
12733
13055
  function buildDeliveredTurnStatePatch(args) {
12734
13056
  const conversation = structuredClone(args.conversation);
@@ -12839,5 +13161,7 @@ export {
12839
13161
  createConversationMemoryService,
12840
13162
  isHumanConversationMessage,
12841
13163
  getConversationMessageSlackTs,
13164
+ scheduleSessionCompletedPluginTasks,
13165
+ processPluginTask,
12842
13166
  buildDeliveredTurnStatePatch
12843
13167
  };