@sentry/junior 0.137.0 → 0.138.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.
- package/dist/{agent-hooks-MZOKEP3J.js → agent-hooks-SCR74Q3E.js} +11 -9
- package/dist/api.js +21 -18
- package/dist/app.d.ts +7 -0
- package/dist/app.js +248 -689
- package/dist/{catalog-runtime-MD66YLFV.js → catalog-runtime-IU3QF4GG.js} +5 -5
- package/dist/chat/agent/request.d.ts +32 -2
- package/dist/chat/agent-dispatch/heartbeat.d.ts +5 -0
- package/dist/chat/agent-dispatch/types.d.ts +1 -1
- package/dist/chat/agent-invocations/errors.d.ts +8 -0
- package/dist/chat/agent-invocations/spawn.d.ts +12 -0
- package/dist/chat/agent-invocations/store.d.ts +44 -0
- package/dist/chat/agent-invocations/types.d.ts +449 -0
- package/dist/chat/agent-invocations/work.d.ts +31 -0
- package/dist/chat/config.d.ts +1 -0
- package/dist/chat/conversations/sql/store.d.ts +8 -0
- package/dist/chat/conversations/store.d.ts +7 -0
- package/dist/chat/experimental.d.ts +16 -0
- package/dist/chat/runtime/agent-runner.d.ts +2 -1
- package/dist/chat/services/auth-pause.d.ts +0 -1
- package/dist/chat/services/mcp-auth-orchestration.d.ts +3 -2
- package/dist/chat/services/plugin-auth-orchestration.d.ts +3 -2
- package/dist/chat/state/turn-session.d.ts +0 -2
- package/dist/chat/task-execution/state.d.ts +23 -11
- package/dist/chat/task-execution/store.d.ts +20 -11
- package/dist/chat/task-execution/worker.d.ts +2 -2
- package/dist/chat/tools/runtime/spawn-agent.d.ts +31 -0
- package/dist/chat/tools/types.d.ts +2 -0
- package/dist/chunk-3Y47VMHV.js +6 -0
- package/dist/chunk-5IDFPCON.js +46 -0
- package/dist/chunk-5LTJ4ITU.js +1523 -0
- package/dist/{chunk-WPPXIAFB.js → chunk-5MXWHTCK.js} +17 -434
- package/dist/{chunk-UOXATPSI.js → chunk-A7WCHXZV.js} +7 -5
- package/dist/{chunk-5G34IEK2.js → chunk-BOLJOV5T.js} +2 -56
- package/dist/{chunk-UG6ZYPBI.js → chunk-BYW4B4PR.js} +73 -60
- package/dist/{chunk-XZ7NPXAB.js → chunk-C7ZVPO4Z.js} +1 -1
- package/dist/{chunk-RB4NJKEX.js → chunk-DCLLQGS6.js} +119 -50
- package/dist/{chunk-DQXAAVTR.js → chunk-E5UYAIKJ.js} +44 -1467
- package/dist/{chunk-6QW2RRQT.js → chunk-ELKW5FIB.js} +5 -5
- package/dist/chunk-EM225ITJ.js +56 -0
- package/dist/{chunk-SYCX4DOK.js → chunk-FEUNPTGV.js} +7 -60
- package/dist/{chunk-YRPC6MJY.js → chunk-FNLBTB4K.js} +1 -1
- package/dist/chunk-HIWJWWMT.js +668 -0
- package/dist/chunk-IUFM5JWV.js +24 -0
- package/dist/{chunk-T6Z5S6PQ.js → chunk-JNNJZURV.js} +1 -1
- package/dist/{chunk-6ZK6FZIV.js → chunk-KCBZSJRU.js} +6 -27
- package/dist/{chunk-5PNJMOY5.js → chunk-KDCHVT25.js} +1 -1
- package/dist/{chunk-BZAMASJP.js → chunk-KFRHRSCJ.js} +1 -1
- package/dist/chunk-LPVUNO2W.js +320 -0
- package/dist/{chunk-EWDSB24B.js → chunk-N7WARMC6.js} +16 -776
- package/dist/chunk-PB3CXNVL.js +41 -0
- package/dist/chunk-Q6QZTTLS.js +55 -0
- package/dist/{chunk-36EG34YI.js → chunk-QS527AV5.js} +1 -1
- package/dist/chunk-R776HKPK.js +81 -0
- package/dist/{chunk-KJ4HGRHN.js → chunk-RZIBSCYC.js} +507 -596
- package/dist/chunk-SD3OFRWY.js +714 -0
- package/dist/{chunk-ZFS67WIR.js → chunk-SIPKWSXK.js} +57 -6
- package/dist/chunk-TCS76X5D.js +441 -0
- package/dist/{chunk-K2NCQJKH.js → chunk-TDJCLNDH.js} +3 -3
- package/dist/{chunk-POBSY47Q.js → chunk-TUUDXEXU.js} +336 -302
- package/dist/{chunk-YNFERGKP.js → chunk-VPZFVJND.js} +1 -1
- package/dist/{chunk-UZQ4VY3M.js → chunk-YU5QUF67.js} +1 -1
- package/dist/chunk-ZBSMCXKV.js +710 -0
- package/dist/{chunk-3Q342EJJ.js → chunk-ZXNTJJIE.js} +2 -2
- package/dist/cli/chat.js +65 -26
- package/dist/cli/check.js +6 -6
- package/dist/cli/plugins.js +14 -12
- package/dist/cli/snapshot-create.js +10 -9
- package/dist/cli/upgrade.js +7 -7
- package/dist/conversation-work-KATFHLG3.js +56 -0
- package/dist/db/schema/agent-invocations.d.ts +544 -0
- package/dist/db/schema.d.ts +543 -1
- package/dist/{db-66E7VDNC.js → db-UO2IWXGA.js} +7 -7
- package/dist/experimental-HH6OM6UH.js +13 -0
- package/dist/instrumentation.js +4 -4
- package/dist/nitro.js +4 -3
- package/dist/{oauth-callback-server-7QPD4Q7P.js → oauth-callback-server-MAQI43LX.js} +26 -18
- package/dist/{runner-L2BKJKOD.js → runner-PIUQEXKE.js} +26 -14
- package/dist/{skills-HOQ2NSPW.js → skills-4ARJADHQ.js} +6 -6
- package/dist/spawn-G2AWPMCT.js +38 -0
- package/dist/{validation-ENHI7S2C.js → validation-S3EE3HWL.js} +6 -6
- package/dist/version.js +1 -1
- package/dist/work-UARRUAW3.js +46 -0
- package/dist/worker-DOCIRVD3.js +24 -0
- package/migrations/0021_first_warhawk.sql +35 -0
- package/migrations/meta/0021_snapshot.json +2176 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +5 -5
|
@@ -1,39 +1,30 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
resolveBaseUrl
|
|
3
|
+
} from "./chunk-SD3OFRWY.js";
|
|
4
|
+
import {
|
|
3
5
|
createPluginLogger,
|
|
4
|
-
getPlugins
|
|
5
|
-
|
|
6
|
-
isDmChannel,
|
|
7
|
-
normalizeSlackConversationId,
|
|
8
|
-
withSlackRetries
|
|
9
|
-
} from "./chunk-WPPXIAFB.js";
|
|
6
|
+
getPlugins
|
|
7
|
+
} from "./chunk-5MXWHTCK.js";
|
|
10
8
|
import {
|
|
11
9
|
getDb,
|
|
12
10
|
parseDestination
|
|
13
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-SIPKWSXK.js";
|
|
14
12
|
import {
|
|
15
|
-
acquireActiveLock,
|
|
16
|
-
getStateAdapter,
|
|
17
13
|
sleep
|
|
18
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-EM225ITJ.js";
|
|
19
15
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
acquireActiveLock,
|
|
17
|
+
getStateAdapter
|
|
18
|
+
} from "./chunk-BOLJOV5T.js";
|
|
22
19
|
import {
|
|
23
20
|
pluginCatalogRuntime
|
|
24
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-QS527AV5.js";
|
|
25
22
|
import {
|
|
26
23
|
fetchWithBoundedOAuthErrorBodies
|
|
27
|
-
} from "./chunk-
|
|
28
|
-
import {
|
|
29
|
-
parseSlackMessageTs,
|
|
30
|
-
parseSlackUserId
|
|
31
|
-
} from "./chunk-F3B2YJ76.js";
|
|
24
|
+
} from "./chunk-KFRHRSCJ.js";
|
|
32
25
|
import {
|
|
33
|
-
isRecord
|
|
34
|
-
|
|
35
|
-
logWarn
|
|
36
|
-
} from "./chunk-T6Z5S6PQ.js";
|
|
26
|
+
isRecord
|
|
27
|
+
} from "./chunk-JNNJZURV.js";
|
|
37
28
|
|
|
38
29
|
// src/chat/capabilities/router.ts
|
|
39
30
|
var ProviderCredentialRouter = class {
|
|
@@ -145,737 +136,6 @@ async function issueProviderCredentialLease(input) {
|
|
|
145
136
|
return await getSandboxEgressRouter().issue(input);
|
|
146
137
|
}
|
|
147
138
|
|
|
148
|
-
// src/chat/agent/request.ts
|
|
149
|
-
var RetryableDeliveryError = class extends Error {
|
|
150
|
-
constructor(cause) {
|
|
151
|
-
super("Assistant delivery was transient or ambiguous", { cause });
|
|
152
|
-
this.name = "RetryableDeliveryError";
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
function actorFromRouting(routing) {
|
|
156
|
-
if (routing.dispatch?.actor) {
|
|
157
|
-
return routing.dispatch.actor;
|
|
158
|
-
}
|
|
159
|
-
if (routing.actor) {
|
|
160
|
-
return routing.actor;
|
|
161
|
-
}
|
|
162
|
-
if (routing.credentialContext && !("type" in routing.credentialContext.actor)) {
|
|
163
|
-
return routing.credentialContext.actor;
|
|
164
|
-
}
|
|
165
|
-
return void 0;
|
|
166
|
-
}
|
|
167
|
-
function assertRunRoutingConsistency(request) {
|
|
168
|
-
const { destination, source } = request.routing;
|
|
169
|
-
if (source.platform !== destination.platform) {
|
|
170
|
-
throw new TypeError("Run source and destination platforms do not match");
|
|
171
|
-
}
|
|
172
|
-
if (source.platform === "slack" && destination.platform === "slack") {
|
|
173
|
-
if (source.teamId !== destination.teamId) {
|
|
174
|
-
throw new TypeError("Slack source and destination teams do not match");
|
|
175
|
-
}
|
|
176
|
-
} else if (source.platform === "local" && destination.platform === "local" && (source.conversationId !== request.conversationId || destination.conversationId !== request.conversationId)) {
|
|
177
|
-
throw new TypeError(
|
|
178
|
-
"Local source, destination, and run conversation IDs do not match"
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
const actor = request.routing.dispatch?.actor ?? request.routing.actor;
|
|
182
|
-
if (!actor || actor.platform === "system") {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
if (actor.platform !== destination.platform) {
|
|
186
|
-
throw new TypeError(
|
|
187
|
-
`Actor platform "${actor.platform}" does not match destination platform "${destination.platform}"`
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
if (actor.platform === "slack" && destination.platform === "slack" && actor.teamId !== destination.teamId) {
|
|
191
|
-
throw new TypeError("Slack actor team does not match destination team");
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
function toolInvocationDestination(routing) {
|
|
195
|
-
if (routing.destination.platform !== "slack" || !routing.toolChannelId) {
|
|
196
|
-
return routing.destination;
|
|
197
|
-
}
|
|
198
|
-
return {
|
|
199
|
-
platform: "slack",
|
|
200
|
-
teamId: routing.destination.teamId,
|
|
201
|
-
channelId: routing.toolChannelId
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
function surfaceFromRouting(routing) {
|
|
205
|
-
if (routing.surface) {
|
|
206
|
-
return routing.surface;
|
|
207
|
-
}
|
|
208
|
-
return routing.source.platform === "slack" ? "slack" : "internal";
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// src/chat/slack/outbound.ts
|
|
212
|
-
var MAX_SLACK_MESSAGE_TEXT_CHARS = 4e4;
|
|
213
|
-
function requireSlackConversationId(channelId, action) {
|
|
214
|
-
const normalized = normalizeSlackConversationId(channelId);
|
|
215
|
-
if (!normalized) {
|
|
216
|
-
throw new Error(`${action} requires a valid channel ID`);
|
|
217
|
-
}
|
|
218
|
-
return normalized;
|
|
219
|
-
}
|
|
220
|
-
function requireSlackThreadTimestamp(threadTs, action) {
|
|
221
|
-
const normalized = threadTs.trim();
|
|
222
|
-
if (!normalized) {
|
|
223
|
-
throw new Error(`${action} requires a thread timestamp`);
|
|
224
|
-
}
|
|
225
|
-
return normalized;
|
|
226
|
-
}
|
|
227
|
-
function requireSlackMessageTimestamp(timestamp, action) {
|
|
228
|
-
const normalized = parseSlackMessageTs(timestamp);
|
|
229
|
-
if (!normalized) {
|
|
230
|
-
throw new Error(`${action} requires a target message timestamp`);
|
|
231
|
-
}
|
|
232
|
-
return normalized;
|
|
233
|
-
}
|
|
234
|
-
function requireSlackMessageText(text, action) {
|
|
235
|
-
if (text.trim().length === 0) {
|
|
236
|
-
throw new Error(`${action} requires non-empty text`);
|
|
237
|
-
}
|
|
238
|
-
if (text.length > MAX_SLACK_MESSAGE_TEXT_CHARS) {
|
|
239
|
-
throw new Error(
|
|
240
|
-
`${action} text exceeds Slack's 40000 character truncation limit`
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
return text;
|
|
244
|
-
}
|
|
245
|
-
async function getSlackMessagePermalink(args) {
|
|
246
|
-
try {
|
|
247
|
-
const response = await withSlackRetries(
|
|
248
|
-
() => getSlackClient().chat.getPermalink({
|
|
249
|
-
channel: args.channelId,
|
|
250
|
-
message_ts: args.messageTs
|
|
251
|
-
}),
|
|
252
|
-
3,
|
|
253
|
-
{
|
|
254
|
-
action: "chat.getPermalink",
|
|
255
|
-
idempotent: true,
|
|
256
|
-
spanAttributes: {
|
|
257
|
-
"app.slack.channel_id": args.channelId,
|
|
258
|
-
"app.slack.message_ts": args.messageTs
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
);
|
|
262
|
-
return response.permalink;
|
|
263
|
-
} catch {
|
|
264
|
-
return void 0;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
async function postSlackMessage(input) {
|
|
268
|
-
const channelId = requireSlackConversationId(
|
|
269
|
-
input.channelId,
|
|
270
|
-
"Slack message posting"
|
|
271
|
-
);
|
|
272
|
-
const text = requireSlackMessageText(input.text, "Slack message posting");
|
|
273
|
-
const threadTs = input.threadTs ? requireSlackThreadTimestamp(
|
|
274
|
-
input.threadTs,
|
|
275
|
-
"Slack thread message posting"
|
|
276
|
-
) : void 0;
|
|
277
|
-
const response = await withSlackRetries(
|
|
278
|
-
() => getSlackClient().chat.postMessage({
|
|
279
|
-
channel: channelId,
|
|
280
|
-
text,
|
|
281
|
-
unfurl_links: false,
|
|
282
|
-
unfurl_media: false,
|
|
283
|
-
...input.blocks?.length ? {
|
|
284
|
-
blocks: input.blocks
|
|
285
|
-
} : {},
|
|
286
|
-
...threadTs ? { thread_ts: threadTs } : {}
|
|
287
|
-
}),
|
|
288
|
-
3,
|
|
289
|
-
{
|
|
290
|
-
action: "chat.postMessage",
|
|
291
|
-
spanAttributes: {
|
|
292
|
-
"app.slack.channel_id": channelId,
|
|
293
|
-
...threadTs ? { "app.slack.thread_ts": threadTs } : {}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
);
|
|
297
|
-
const messageTs = parseSlackMessageTs(response.ts);
|
|
298
|
-
if (!messageTs) {
|
|
299
|
-
throw new Error("Slack message posted without ts");
|
|
300
|
-
}
|
|
301
|
-
return {
|
|
302
|
-
ts: messageTs,
|
|
303
|
-
...input.includePermalink ? {
|
|
304
|
-
permalink: await getSlackMessagePermalink({
|
|
305
|
-
channelId,
|
|
306
|
-
messageTs
|
|
307
|
-
})
|
|
308
|
-
} : {}
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
async function postSlackEphemeralMessage(input) {
|
|
312
|
-
const channelId = requireSlackConversationId(
|
|
313
|
-
input.channelId,
|
|
314
|
-
"Slack ephemeral message posting"
|
|
315
|
-
);
|
|
316
|
-
const userId = parseSlackUserId(input.userId);
|
|
317
|
-
if (!userId) {
|
|
318
|
-
throw new Error("Slack ephemeral message posting requires a user ID");
|
|
319
|
-
}
|
|
320
|
-
const text = requireSlackMessageText(
|
|
321
|
-
input.text,
|
|
322
|
-
"Slack ephemeral message posting"
|
|
323
|
-
);
|
|
324
|
-
const threadTs = input.threadTs ? requireSlackThreadTimestamp(
|
|
325
|
-
input.threadTs,
|
|
326
|
-
"Slack ephemeral thread message posting"
|
|
327
|
-
) : void 0;
|
|
328
|
-
const response = await withSlackRetries(
|
|
329
|
-
() => getSlackClient().chat.postEphemeral({
|
|
330
|
-
channel: channelId,
|
|
331
|
-
user: userId,
|
|
332
|
-
text,
|
|
333
|
-
...threadTs ? { thread_ts: threadTs } : {}
|
|
334
|
-
}),
|
|
335
|
-
3,
|
|
336
|
-
{
|
|
337
|
-
action: "chat.postEphemeral",
|
|
338
|
-
spanAttributes: {
|
|
339
|
-
"app.slack.channel_id": channelId,
|
|
340
|
-
"app.slack.user_id": userId,
|
|
341
|
-
...threadTs ? { "app.slack.thread_ts": threadTs } : {}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
);
|
|
345
|
-
return {
|
|
346
|
-
messageTs: response.message_ts
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
async function uploadFilesToConversation(input) {
|
|
350
|
-
const channelId = requireSlackConversationId(
|
|
351
|
-
input.channelId,
|
|
352
|
-
"Slack file upload"
|
|
353
|
-
);
|
|
354
|
-
const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack file upload") : void 0;
|
|
355
|
-
if (input.files.length === 0) {
|
|
356
|
-
throw new Error("Slack file upload requires at least one file");
|
|
357
|
-
}
|
|
358
|
-
const fileUploads = input.files.map((file) => {
|
|
359
|
-
const filename = file.filename.trim();
|
|
360
|
-
if (!filename) {
|
|
361
|
-
throw new Error(
|
|
362
|
-
"Slack file upload requires every file to have a filename"
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
return {
|
|
366
|
-
file: file.data,
|
|
367
|
-
filename
|
|
368
|
-
};
|
|
369
|
-
});
|
|
370
|
-
const response = await withSlackRetries(
|
|
371
|
-
() => getSlackClient().filesUploadV2({
|
|
372
|
-
channel_id: channelId,
|
|
373
|
-
...threadTs ? { thread_ts: threadTs } : {},
|
|
374
|
-
file_uploads: fileUploads
|
|
375
|
-
}),
|
|
376
|
-
3,
|
|
377
|
-
{
|
|
378
|
-
action: "filesUploadV2",
|
|
379
|
-
spanAttributes: {
|
|
380
|
-
"app.slack.channel_id": channelId,
|
|
381
|
-
...threadTs ? { "app.slack.thread_ts": threadTs } : {}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
);
|
|
385
|
-
return {
|
|
386
|
-
files: (response.files ?? []).flatMap(
|
|
387
|
-
(completion) => (completion.files ?? []).map((file) => file.id ? { id: file.id } : {})
|
|
388
|
-
)
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
async function addReactionToMessage(input) {
|
|
392
|
-
const channelId = requireSlackConversationId(
|
|
393
|
-
input.channelId,
|
|
394
|
-
"Slack reaction"
|
|
395
|
-
);
|
|
396
|
-
const timestamp = requireSlackMessageTimestamp(
|
|
397
|
-
input.timestamp,
|
|
398
|
-
"Slack reaction"
|
|
399
|
-
);
|
|
400
|
-
const emoji = normalizeSlackEmojiName(input.emoji);
|
|
401
|
-
if (!emoji) {
|
|
402
|
-
throw new Error("Slack reaction requires a valid emoji alias name");
|
|
403
|
-
}
|
|
404
|
-
try {
|
|
405
|
-
await withSlackRetries(
|
|
406
|
-
() => getSlackClient().reactions.add({
|
|
407
|
-
channel: channelId,
|
|
408
|
-
timestamp,
|
|
409
|
-
name: emoji
|
|
410
|
-
}),
|
|
411
|
-
3,
|
|
412
|
-
{
|
|
413
|
-
action: "reactions.add",
|
|
414
|
-
idempotent: true,
|
|
415
|
-
spanAttributes: {
|
|
416
|
-
"app.slack.channel_id": channelId,
|
|
417
|
-
"app.slack.message_ts": timestamp,
|
|
418
|
-
"app.slack.reaction": emoji
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
);
|
|
422
|
-
} catch (error) {
|
|
423
|
-
if (error instanceof SlackActionError && error.code === "already_reacted") {
|
|
424
|
-
return { ok: true };
|
|
425
|
-
}
|
|
426
|
-
throw error;
|
|
427
|
-
}
|
|
428
|
-
return { ok: true };
|
|
429
|
-
}
|
|
430
|
-
async function removeReactionFromMessage(input) {
|
|
431
|
-
const channelId = requireSlackConversationId(
|
|
432
|
-
input.channelId,
|
|
433
|
-
"Slack reaction removal"
|
|
434
|
-
);
|
|
435
|
-
const timestamp = requireSlackMessageTimestamp(
|
|
436
|
-
input.timestamp,
|
|
437
|
-
"Slack reaction removal"
|
|
438
|
-
);
|
|
439
|
-
const emoji = normalizeSlackEmojiName(input.emoji);
|
|
440
|
-
if (!emoji) {
|
|
441
|
-
throw new Error("Slack reaction removal requires a valid emoji alias name");
|
|
442
|
-
}
|
|
443
|
-
try {
|
|
444
|
-
await withSlackRetries(
|
|
445
|
-
() => getSlackClient().reactions.remove({
|
|
446
|
-
channel: channelId,
|
|
447
|
-
timestamp,
|
|
448
|
-
name: emoji
|
|
449
|
-
}),
|
|
450
|
-
3,
|
|
451
|
-
{
|
|
452
|
-
action: "reactions.remove",
|
|
453
|
-
idempotent: true,
|
|
454
|
-
spanAttributes: {
|
|
455
|
-
"app.slack.channel_id": channelId,
|
|
456
|
-
"app.slack.message_ts": timestamp,
|
|
457
|
-
"app.slack.reaction": emoji
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
);
|
|
461
|
-
} catch (error) {
|
|
462
|
-
if (error instanceof SlackActionError && error.code === "no_reaction") {
|
|
463
|
-
return { ok: true };
|
|
464
|
-
}
|
|
465
|
-
throw error;
|
|
466
|
-
}
|
|
467
|
-
return { ok: true };
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// src/chat/oauth-flow.ts
|
|
471
|
-
import { randomBytes } from "crypto";
|
|
472
|
-
import {
|
|
473
|
-
sourceSchema
|
|
474
|
-
} from "@sentry/junior-plugin-api";
|
|
475
|
-
|
|
476
|
-
// src/chat/slack/status-format.ts
|
|
477
|
-
var SLACK_STATUS_MAX_LENGTH = 50;
|
|
478
|
-
function truncateStatusText(text) {
|
|
479
|
-
const trimmed = text.trim();
|
|
480
|
-
if (!trimmed) {
|
|
481
|
-
return "";
|
|
482
|
-
}
|
|
483
|
-
if (trimmed.length <= SLACK_STATUS_MAX_LENGTH) {
|
|
484
|
-
return trimmed;
|
|
485
|
-
}
|
|
486
|
-
return `${trimmed.slice(0, SLACK_STATUS_MAX_LENGTH - 3).trimEnd()}...`;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// src/chat/slack/mrkdwn.ts
|
|
490
|
-
function escapeSlackMrkdwnText(text) {
|
|
491
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
492
|
-
}
|
|
493
|
-
function escapeSlackLinkUrl(url) {
|
|
494
|
-
return url.replaceAll("&", "&").replaceAll("<", "%3C").replaceAll(">", "%3E");
|
|
495
|
-
}
|
|
496
|
-
function formatSlackLink(url, label) {
|
|
497
|
-
return `<${escapeSlackLinkUrl(url)}|${escapeSlackMrkdwnText(label)}>`;
|
|
498
|
-
}
|
|
499
|
-
function readInlineCodeSpan(line, start) {
|
|
500
|
-
if (line[start] !== "`") {
|
|
501
|
-
return void 0;
|
|
502
|
-
}
|
|
503
|
-
let n = 1;
|
|
504
|
-
while (line[start + n] === "`") {
|
|
505
|
-
n++;
|
|
506
|
-
}
|
|
507
|
-
const marker = "`".repeat(n);
|
|
508
|
-
let search = start + n;
|
|
509
|
-
while (search < line.length) {
|
|
510
|
-
const close = line.indexOf(marker, search);
|
|
511
|
-
if (close === -1) {
|
|
512
|
-
return void 0;
|
|
513
|
-
}
|
|
514
|
-
const after = close + n;
|
|
515
|
-
if (line[after] !== "`") {
|
|
516
|
-
return { text: line.slice(start, after), end: after };
|
|
517
|
-
}
|
|
518
|
-
search = after + 1;
|
|
519
|
-
}
|
|
520
|
-
return void 0;
|
|
521
|
-
}
|
|
522
|
-
function readExistingSlackAngleToken(line, start) {
|
|
523
|
-
if (line[start] !== "<") {
|
|
524
|
-
return void 0;
|
|
525
|
-
}
|
|
526
|
-
const close = line.indexOf(">", start + 1);
|
|
527
|
-
if (close === -1) {
|
|
528
|
-
return void 0;
|
|
529
|
-
}
|
|
530
|
-
const body = line.slice(start + 1, close);
|
|
531
|
-
if (/^(?:https?:\/\/|@|#|!)/.test(body)) {
|
|
532
|
-
return { text: line.slice(start, close + 1), end: close + 1 };
|
|
533
|
-
}
|
|
534
|
-
return void 0;
|
|
535
|
-
}
|
|
536
|
-
function readMarkdownLink(line, start) {
|
|
537
|
-
if (line[start] !== "[") {
|
|
538
|
-
return void 0;
|
|
539
|
-
}
|
|
540
|
-
const labelEnd = line.indexOf("](", start + 1);
|
|
541
|
-
if (labelEnd === -1) {
|
|
542
|
-
return void 0;
|
|
543
|
-
}
|
|
544
|
-
const destStart = labelEnd + 2;
|
|
545
|
-
if (!line.startsWith("http://", destStart) && !line.startsWith("https://", destStart)) {
|
|
546
|
-
return void 0;
|
|
547
|
-
}
|
|
548
|
-
const closeParens = line.indexOf(")", destStart);
|
|
549
|
-
if (closeParens === -1) {
|
|
550
|
-
return void 0;
|
|
551
|
-
}
|
|
552
|
-
return { text: line.slice(start, closeParens + 1), end: closeParens + 1 };
|
|
553
|
-
}
|
|
554
|
-
function hasUnmatchedClosingParen(text) {
|
|
555
|
-
let balance = 0;
|
|
556
|
-
for (const ch of text) {
|
|
557
|
-
if (ch === "(") balance++;
|
|
558
|
-
else if (ch === ")") balance--;
|
|
559
|
-
}
|
|
560
|
-
return balance < 0;
|
|
561
|
-
}
|
|
562
|
-
function readBareUrl(line, start) {
|
|
563
|
-
let end = start;
|
|
564
|
-
while (end < line.length) {
|
|
565
|
-
const ch = line[end];
|
|
566
|
-
if (/\s/.test(ch) || ch === "<" || ch === ">" || ch === '"' || ch === "`" || ch === "|" || ch === "*") {
|
|
567
|
-
break;
|
|
568
|
-
}
|
|
569
|
-
end++;
|
|
570
|
-
}
|
|
571
|
-
if (end === start) {
|
|
572
|
-
return void 0;
|
|
573
|
-
}
|
|
574
|
-
let raw = line.slice(start, end);
|
|
575
|
-
let suffix = "";
|
|
576
|
-
const peel = () => {
|
|
577
|
-
suffix = raw.slice(-1) + suffix;
|
|
578
|
-
raw = raw.slice(0, -1);
|
|
579
|
-
};
|
|
580
|
-
const shouldPeel = () => raw.endsWith("_") || /[.,!?;:]$/.test(raw) || raw.endsWith(")") && hasUnmatchedClosingParen(raw);
|
|
581
|
-
while (raw.length > 0 && shouldPeel()) {
|
|
582
|
-
peel();
|
|
583
|
-
}
|
|
584
|
-
if (!/^https?:\/\/.+/.test(raw)) {
|
|
585
|
-
return void 0;
|
|
586
|
-
}
|
|
587
|
-
return { url: raw, suffix, end };
|
|
588
|
-
}
|
|
589
|
-
function wrapBareUrlsOnLine(line) {
|
|
590
|
-
let result = "";
|
|
591
|
-
let i = 0;
|
|
592
|
-
while (i < line.length) {
|
|
593
|
-
const codeSpan = readInlineCodeSpan(line, i);
|
|
594
|
-
if (codeSpan) {
|
|
595
|
-
result += codeSpan.text;
|
|
596
|
-
i = codeSpan.end;
|
|
597
|
-
continue;
|
|
598
|
-
}
|
|
599
|
-
const angleToken = readExistingSlackAngleToken(line, i);
|
|
600
|
-
if (angleToken) {
|
|
601
|
-
result += angleToken.text;
|
|
602
|
-
i = angleToken.end;
|
|
603
|
-
continue;
|
|
604
|
-
}
|
|
605
|
-
const mdLink = readMarkdownLink(line, i);
|
|
606
|
-
if (mdLink) {
|
|
607
|
-
result += mdLink.text;
|
|
608
|
-
i = mdLink.end;
|
|
609
|
-
continue;
|
|
610
|
-
}
|
|
611
|
-
if (line.startsWith("https://", i) || line.startsWith("http://", i)) {
|
|
612
|
-
const parsed = readBareUrl(line, i);
|
|
613
|
-
if (parsed) {
|
|
614
|
-
result += `<${parsed.url}>${parsed.suffix}`;
|
|
615
|
-
i = parsed.end;
|
|
616
|
-
continue;
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
result += line[i];
|
|
620
|
-
i++;
|
|
621
|
-
}
|
|
622
|
-
return result;
|
|
623
|
-
}
|
|
624
|
-
function wrapBareUrls(text) {
|
|
625
|
-
const lines = text.split("\n");
|
|
626
|
-
const out = [];
|
|
627
|
-
let inCodeBlock = false;
|
|
628
|
-
for (const line of lines) {
|
|
629
|
-
if (line.trimStart().startsWith("```")) {
|
|
630
|
-
inCodeBlock = !inCodeBlock;
|
|
631
|
-
out.push(line);
|
|
632
|
-
continue;
|
|
633
|
-
}
|
|
634
|
-
out.push(inCodeBlock ? line : wrapBareUrlsOnLine(line));
|
|
635
|
-
}
|
|
636
|
-
return out.join("\n");
|
|
637
|
-
}
|
|
638
|
-
function ensureBlockSpacing(text) {
|
|
639
|
-
const codeBlockPattern = /^```/;
|
|
640
|
-
const listItemPattern = /^[-*•]\s|^\d+\.\s/;
|
|
641
|
-
const lines = text.split("\n");
|
|
642
|
-
const result = [];
|
|
643
|
-
let inCodeBlock = false;
|
|
644
|
-
for (let i = 0; i < lines.length; i++) {
|
|
645
|
-
const line = lines[i];
|
|
646
|
-
const isCodeFence = codeBlockPattern.test(line.trimStart());
|
|
647
|
-
if (isCodeFence) {
|
|
648
|
-
if (!inCodeBlock) {
|
|
649
|
-
const prev2 = result.length > 0 ? result[result.length - 1] : void 0;
|
|
650
|
-
if (prev2 !== void 0 && prev2.trim() !== "") {
|
|
651
|
-
result.push("");
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
inCodeBlock = !inCodeBlock;
|
|
655
|
-
result.push(line);
|
|
656
|
-
continue;
|
|
657
|
-
}
|
|
658
|
-
if (inCodeBlock) {
|
|
659
|
-
result.push(line);
|
|
660
|
-
continue;
|
|
661
|
-
}
|
|
662
|
-
const prev = result.length > 0 ? result[result.length - 1] : void 0;
|
|
663
|
-
if (prev !== void 0 && prev.trim() !== "" && line.trim() !== "" && !(listItemPattern.test(prev.trimStart()) && listItemPattern.test(line.trimStart()))) {
|
|
664
|
-
result.push("");
|
|
665
|
-
}
|
|
666
|
-
result.push(line);
|
|
667
|
-
}
|
|
668
|
-
return result.join("\n");
|
|
669
|
-
}
|
|
670
|
-
function normalizeSlackReplyMarkdown(text) {
|
|
671
|
-
let normalized = text.replace(/\r\n?/g, "\n").replace(/[ \t]+$/gm, "");
|
|
672
|
-
normalized = wrapBareUrls(normalized);
|
|
673
|
-
normalized = ensureBlockSpacing(normalized);
|
|
674
|
-
return normalized.replace(/\n{3,}/g, "\n\n").trim();
|
|
675
|
-
}
|
|
676
|
-
function normalizeSlackStatusText(text) {
|
|
677
|
-
const trimmed = text.trim();
|
|
678
|
-
if (!trimmed) {
|
|
679
|
-
return "";
|
|
680
|
-
}
|
|
681
|
-
return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// src/chat/slack/oauth-authorization-message.ts
|
|
685
|
-
function formatOAuthAuthorizationMessage(args) {
|
|
686
|
-
return `${formatSlackLink(args.authorizationUrl, args.label)}. ${args.completionText}`;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// src/chat/oauth-flow.ts
|
|
690
|
-
var OAUTH_STATE_TTL_MS = 10 * 60 * 1e3;
|
|
691
|
-
function optionalString(value) {
|
|
692
|
-
return typeof value === "string" ? value : void 0;
|
|
693
|
-
}
|
|
694
|
-
function parseOAuthStatePayload(value) {
|
|
695
|
-
if (!isRecord(value)) {
|
|
696
|
-
return void 0;
|
|
697
|
-
}
|
|
698
|
-
if (typeof value.userId !== "string" || typeof value.provider !== "string") {
|
|
699
|
-
return void 0;
|
|
700
|
-
}
|
|
701
|
-
const destination = parseDestination(value.destination);
|
|
702
|
-
if (value.destination !== void 0 && !destination) {
|
|
703
|
-
return void 0;
|
|
704
|
-
}
|
|
705
|
-
const source = value.source === void 0 ? void 0 : sourceSchema.safeParse(value.source);
|
|
706
|
-
if (value.source !== void 0 && (!source || !source.success)) {
|
|
707
|
-
return void 0;
|
|
708
|
-
}
|
|
709
|
-
return {
|
|
710
|
-
userId: value.userId,
|
|
711
|
-
provider: value.provider,
|
|
712
|
-
...optionalString(value.channelId) ? { channelId: optionalString(value.channelId) } : {},
|
|
713
|
-
...destination ? { destination } : {},
|
|
714
|
-
...source?.success ? { source: source.data } : {},
|
|
715
|
-
...optionalString(value.threadTs) ? { threadTs: optionalString(value.threadTs) } : {},
|
|
716
|
-
...optionalString(value.resumeConversationId) ? { resumeConversationId: optionalString(value.resumeConversationId) } : {},
|
|
717
|
-
...optionalString(value.resumeSessionId) ? { resumeSessionId: optionalString(value.resumeSessionId) } : {},
|
|
718
|
-
...optionalString(value.scope) ? { scope: optionalString(value.scope) } : {}
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
function formatProviderLabel(provider) {
|
|
722
|
-
const displayName = pluginCatalogRuntime.getDisplayName(provider);
|
|
723
|
-
if (!displayName) {
|
|
724
|
-
throw new Error(`Unknown plugin provider display name: "${provider}"`);
|
|
725
|
-
}
|
|
726
|
-
return displayName;
|
|
727
|
-
}
|
|
728
|
-
function resolveBaseUrl() {
|
|
729
|
-
const explicit = process.env.JUNIOR_BASE_URL?.trim();
|
|
730
|
-
if (explicit) return explicit;
|
|
731
|
-
const vercelProd = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
|
|
732
|
-
if (vercelProd) return `https://${vercelProd}`;
|
|
733
|
-
const vercelUrl = process.env.VERCEL_URL?.trim();
|
|
734
|
-
if (vercelUrl) return `https://${vercelUrl}`;
|
|
735
|
-
return void 0;
|
|
736
|
-
}
|
|
737
|
-
async function deliverPrivateMessage(input) {
|
|
738
|
-
let client;
|
|
739
|
-
try {
|
|
740
|
-
client = getSlackClient();
|
|
741
|
-
} catch {
|
|
742
|
-
logWarn("oauth.private_delivery.skipped", {
|
|
743
|
-
"app.reason": "missing_bot_token"
|
|
744
|
-
});
|
|
745
|
-
return false;
|
|
746
|
-
}
|
|
747
|
-
if (input.channelId) {
|
|
748
|
-
try {
|
|
749
|
-
if (isDmChannel(input.channelId)) {
|
|
750
|
-
await postSlackMessage({
|
|
751
|
-
channelId: input.channelId,
|
|
752
|
-
text: input.text,
|
|
753
|
-
threadTs: input.threadTs
|
|
754
|
-
});
|
|
755
|
-
} else {
|
|
756
|
-
await postSlackEphemeralMessage({
|
|
757
|
-
channelId: input.channelId,
|
|
758
|
-
userId: input.userId,
|
|
759
|
-
text: input.text,
|
|
760
|
-
threadTs: input.threadTs
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
return "in_context";
|
|
764
|
-
} catch (error) {
|
|
765
|
-
logWarn("oauth.private_delivery.failed", {
|
|
766
|
-
"app.slack.error": error instanceof Error ? error.message : String(error),
|
|
767
|
-
"app.slack.channel": input.channelId
|
|
768
|
-
});
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
try {
|
|
772
|
-
const dmChannelId = (await withSlackRetries(
|
|
773
|
-
() => client.conversations.open({ users: input.userId }),
|
|
774
|
-
3,
|
|
775
|
-
{
|
|
776
|
-
action: "conversations.open",
|
|
777
|
-
spanAttributes: { "app.slack.user_id": input.userId }
|
|
778
|
-
}
|
|
779
|
-
)).channel?.id;
|
|
780
|
-
if (!dmChannelId) {
|
|
781
|
-
logWarn("oauth.dm.fallback.failed", { "app.reason": "no_dm_channel_id" });
|
|
782
|
-
return false;
|
|
783
|
-
}
|
|
784
|
-
await postSlackMessage({ channelId: dmChannelId, text: input.text });
|
|
785
|
-
return "fallback_dm";
|
|
786
|
-
} catch (error) {
|
|
787
|
-
logWarn("oauth.dm.fallback.failed", {
|
|
788
|
-
"app.slack.error": error instanceof Error ? error.message : String(error)
|
|
789
|
-
});
|
|
790
|
-
return false;
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
async function deliverOAuthAuthorization(request, input) {
|
|
794
|
-
if (input.authorization) {
|
|
795
|
-
await input.authorization.deliver(request);
|
|
796
|
-
return "in_context";
|
|
797
|
-
}
|
|
798
|
-
return await deliverPrivateMessage({
|
|
799
|
-
channelId: input.channelId,
|
|
800
|
-
threadTs: input.threadTs,
|
|
801
|
-
userId: input.userId,
|
|
802
|
-
text: formatOAuthAuthorizationMessage(request)
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
async function startOAuthFlow(provider, input) {
|
|
806
|
-
const providerConfig = pluginCatalogRuntime.getOAuthConfig(provider);
|
|
807
|
-
if (!providerConfig) {
|
|
808
|
-
return {
|
|
809
|
-
ok: false,
|
|
810
|
-
error: `Provider "${provider}" does not support OAuth authorization`
|
|
811
|
-
};
|
|
812
|
-
}
|
|
813
|
-
const clientId = process.env[providerConfig.clientIdEnv]?.trim();
|
|
814
|
-
if (!clientId) {
|
|
815
|
-
return {
|
|
816
|
-
ok: false,
|
|
817
|
-
error: `Missing ${providerConfig.clientIdEnv} environment variable`
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
const baseUrl = resolveBaseUrl();
|
|
821
|
-
if (!baseUrl) {
|
|
822
|
-
return {
|
|
823
|
-
ok: false,
|
|
824
|
-
error: "Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
|
|
825
|
-
};
|
|
826
|
-
}
|
|
827
|
-
const state = input.authorization ? await input.authorization.createState() : randomBytes(32).toString("hex");
|
|
828
|
-
const requestedScope = input.scope ?? providerConfig.scope;
|
|
829
|
-
await getStateAdapter().set(
|
|
830
|
-
`oauth-state:${state}`,
|
|
831
|
-
{
|
|
832
|
-
userId: input.actorId,
|
|
833
|
-
provider,
|
|
834
|
-
...input.channelId ? { channelId: input.channelId } : {},
|
|
835
|
-
...input.destination ? { destination: input.destination } : {},
|
|
836
|
-
...input.source ? { source: input.source } : {},
|
|
837
|
-
...input.threadTs ? { threadTs: input.threadTs } : {},
|
|
838
|
-
...input.resumeConversationId ? { resumeConversationId: input.resumeConversationId } : {},
|
|
839
|
-
...input.resumeSessionId ? { resumeSessionId: input.resumeSessionId } : {},
|
|
840
|
-
...requestedScope ? { scope: requestedScope } : {}
|
|
841
|
-
},
|
|
842
|
-
OAUTH_STATE_TTL_MS
|
|
843
|
-
);
|
|
844
|
-
const authorizeParams = new URLSearchParams({
|
|
845
|
-
client_id: clientId,
|
|
846
|
-
state,
|
|
847
|
-
redirect_uri: `${baseUrl}${providerConfig.callbackPath}`,
|
|
848
|
-
response_type: "code"
|
|
849
|
-
});
|
|
850
|
-
if (requestedScope) {
|
|
851
|
-
authorizeParams.set("scope", requestedScope);
|
|
852
|
-
}
|
|
853
|
-
for (const [key, value] of Object.entries(
|
|
854
|
-
providerConfig.authorizeParams ?? {}
|
|
855
|
-
)) {
|
|
856
|
-
authorizeParams.set(key, value);
|
|
857
|
-
}
|
|
858
|
-
logInfo("jr_rpc.oauth.started", {
|
|
859
|
-
"app.credential.provider": provider,
|
|
860
|
-
...input.activeSkillName ? { "app.skill.name": input.activeSkillName } : {}
|
|
861
|
-
});
|
|
862
|
-
const authorizationUrl = `${providerConfig.authorizeEndpoint}?${authorizeParams.toString()}`;
|
|
863
|
-
const authorizationRequest = {
|
|
864
|
-
authorizationUrl,
|
|
865
|
-
label: `Click here to link your ${formatProviderLabel(provider)} account`,
|
|
866
|
-
completionText: input.resumeSessionId ? "Once you've authorized, Junior will continue automatically." : "Once you've authorized, you'll see a confirmation in Slack."
|
|
867
|
-
};
|
|
868
|
-
return {
|
|
869
|
-
ok: true,
|
|
870
|
-
delivery: await deliverOAuthAuthorization(authorizationRequest, {
|
|
871
|
-
authorization: input.authorization,
|
|
872
|
-
channelId: input.channelId,
|
|
873
|
-
threadTs: input.threadTs,
|
|
874
|
-
userId: input.actorId
|
|
875
|
-
})
|
|
876
|
-
};
|
|
877
|
-
}
|
|
878
|
-
|
|
879
139
|
// src/chat/mcp/errors.ts
|
|
880
140
|
var McpProviderError = class extends Error {
|
|
881
141
|
phase;
|
|
@@ -951,7 +211,7 @@ function getMcpAwareTelemetryMessage(error, _privacy) {
|
|
|
951
211
|
|
|
952
212
|
// src/chat/mcp/auth-store.ts
|
|
953
213
|
import {
|
|
954
|
-
sourceSchema
|
|
214
|
+
sourceSchema
|
|
955
215
|
} from "@sentry/junior-plugin-api";
|
|
956
216
|
var MCP_AUTH_SESSION_PREFIX = "junior:mcp_oauth_attempt:v2";
|
|
957
217
|
var MCP_AUTH_CREDENTIALS_PREFIX = "junior:mcp_auth_credentials";
|
|
@@ -1004,7 +264,7 @@ function parseMcpAuthSession(value) {
|
|
|
1004
264
|
if (parsed.destination !== void 0 && !destination) {
|
|
1005
265
|
return void 0;
|
|
1006
266
|
}
|
|
1007
|
-
const source = parsed.source === void 0 ? void 0 :
|
|
267
|
+
const source = parsed.source === void 0 ? void 0 : sourceSchema.safeParse(parsed.source);
|
|
1008
268
|
if (parsed.source !== void 0 && (!source || !source.success)) {
|
|
1009
269
|
return void 0;
|
|
1010
270
|
}
|
|
@@ -1689,28 +949,8 @@ async function issuePluginCredential(input) {
|
|
|
1689
949
|
}
|
|
1690
950
|
|
|
1691
951
|
export {
|
|
1692
|
-
RetryableDeliveryError,
|
|
1693
|
-
actorFromRouting,
|
|
1694
|
-
assertRunRoutingConsistency,
|
|
1695
|
-
toolInvocationDestination,
|
|
1696
|
-
surfaceFromRouting,
|
|
1697
952
|
createUserTokenStore,
|
|
1698
953
|
issueProviderCredentialLease,
|
|
1699
|
-
getSlackMessagePermalink,
|
|
1700
|
-
postSlackMessage,
|
|
1701
|
-
uploadFilesToConversation,
|
|
1702
|
-
addReactionToMessage,
|
|
1703
|
-
removeReactionFromMessage,
|
|
1704
|
-
truncateStatusText,
|
|
1705
|
-
escapeSlackMrkdwnText,
|
|
1706
|
-
formatSlackLink,
|
|
1707
|
-
normalizeSlackReplyMarkdown,
|
|
1708
|
-
normalizeSlackStatusText,
|
|
1709
|
-
parseOAuthStatePayload,
|
|
1710
|
-
formatProviderLabel,
|
|
1711
|
-
resolveBaseUrl,
|
|
1712
|
-
deliverOAuthAuthorization,
|
|
1713
|
-
startOAuthFlow,
|
|
1714
954
|
McpProviderError,
|
|
1715
955
|
toMcpProviderError,
|
|
1716
956
|
getMcpProviderErrorAttributes,
|