@mastra/slack 1.5.1 → 1.6.0-alpha.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/CHANGELOG.md +20 -0
- package/dist/index.cjs +191 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +183 -40
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @mastra/slack
|
|
2
2
|
|
|
3
|
+
## 1.6.0-alpha.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Slack connections can now be owned by an `AgentController`, not just a registered `Agent`. Call `connect` with an options object to connect an owner that has no registered agent: ([#19289](https://github.com/mastra-ai/mastra/pull/19289))
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
await mastra.channels.slack.connect({
|
|
11
|
+
id: 'my-controller',
|
|
12
|
+
name: 'Support Bot',
|
|
13
|
+
});
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Installations record an `ownerType` (`'agent'` by default, or `'agentController'`). Inbound Slack events and OAuth callbacks route to the owning `AgentController`'s channels when `ownerType` is `'agentController'`, so mentions and messages drive a controller session. Existing agent-owned installations are unaffected.
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [[`ce93a3c`](https://github.com/mastra-ai/mastra/commit/ce93a3c114ea1cbfbd576f3db41d7c26c9844f5b), [`5718a22`](https://github.com/mastra-ai/mastra/commit/5718a229281dcfd36bcd1f42a242e3717e510a33), [`5807d3a`](https://github.com/mastra-ai/mastra/commit/5807d3ae1d259b8b7d6df7e5bf2b485c694af9c8), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`5718a22`](https://github.com/mastra-ai/mastra/commit/5718a229281dcfd36bcd1f42a242e3717e510a33), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`d1b7e3a`](https://github.com/mastra-ai/mastra/commit/d1b7e3a978a309a5653eeaa490d2d6c7c53bd093), [`c093146`](https://github.com/mastra-ai/mastra/commit/c0931466404d3c521308ea119cb165bb7e695155)]:
|
|
21
|
+
- @mastra/core@1.54.0-alpha.1
|
|
22
|
+
|
|
3
23
|
## 1.5.1
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -181,6 +181,33 @@ var SlackManifestClient = class {
|
|
|
181
181
|
throw new Error(`App deletion failed: ${data.error}`);
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Check whether a Slack app still exists.
|
|
186
|
+
*
|
|
187
|
+
* Uses the read-only `apps.manifest.export` endpoint as an existence probe.
|
|
188
|
+
* Returns `false` when Slack reports the app is missing/inaccessible
|
|
189
|
+
* (e.g. it was deleted from the Slack admin UI), `true` when it exports
|
|
190
|
+
* successfully. Network/transport errors are re-thrown so callers can
|
|
191
|
+
* distinguish "app is gone" from "couldn't reach Slack".
|
|
192
|
+
*/
|
|
193
|
+
async appExists(appId) {
|
|
194
|
+
await this.rotateToken();
|
|
195
|
+
const response = await fetch(`${SLACK_API_BASE}/apps.manifest.export`, {
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: {
|
|
198
|
+
"Content-Type": "application/json",
|
|
199
|
+
Authorization: `Bearer ${this.#token}`
|
|
200
|
+
},
|
|
201
|
+
body: JSON.stringify({ app_id: appId }),
|
|
202
|
+
signal: AbortSignal.timeout(SLACK_API_TIMEOUT_MS)
|
|
203
|
+
});
|
|
204
|
+
const data = await response.json();
|
|
205
|
+
if (data.ok) return true;
|
|
206
|
+
if (data.error === "app_not_found" || data.error === "invalid_app_id") {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
throw new Error(`Failed to check whether Slack app exists: ${data.error ?? "unknown_error"}`);
|
|
210
|
+
}
|
|
184
211
|
/**
|
|
185
212
|
* Update an existing Slack app's manifest.
|
|
186
213
|
*/
|
|
@@ -228,7 +255,7 @@ var SlackManifestClient = class {
|
|
|
228
255
|
};
|
|
229
256
|
|
|
230
257
|
// src/crypto.ts
|
|
231
|
-
var
|
|
258
|
+
var import_node_crypto = require("crypto");
|
|
232
259
|
function verifySlackRequest(params) {
|
|
233
260
|
const { signingSecret, timestamp, body, signature } = params;
|
|
234
261
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -237,9 +264,9 @@ function verifySlackRequest(params) {
|
|
|
237
264
|
return false;
|
|
238
265
|
}
|
|
239
266
|
const sigBasestring = `v0:${timestamp}:${body}`;
|
|
240
|
-
const expectedSignature = `v0=${(0,
|
|
267
|
+
const expectedSignature = `v0=${(0, import_node_crypto.createHmac)("sha256", signingSecret).update(sigBasestring).digest("hex")}`;
|
|
241
268
|
try {
|
|
242
|
-
return (0,
|
|
269
|
+
return (0, import_node_crypto.timingSafeEqual)(Buffer.from(signature), Buffer.from(expectedSignature));
|
|
243
270
|
} catch {
|
|
244
271
|
return false;
|
|
245
272
|
}
|
|
@@ -253,10 +280,10 @@ function parseSlackFormBody(body) {
|
|
|
253
280
|
}
|
|
254
281
|
var ALGO_PREFIX = "aes-256-gcm-hkdf";
|
|
255
282
|
function encrypt(plaintext, key) {
|
|
256
|
-
const salt = (0,
|
|
257
|
-
const derived = Buffer.from((0,
|
|
258
|
-
const iv = (0,
|
|
259
|
-
const cipher = (0,
|
|
283
|
+
const salt = (0, import_node_crypto.randomBytes)(16);
|
|
284
|
+
const derived = Buffer.from((0, import_node_crypto.hkdfSync)("sha256", key, salt, "mastra-slack-encryption", 32));
|
|
285
|
+
const iv = (0, import_node_crypto.randomBytes)(12);
|
|
286
|
+
const cipher = (0, import_node_crypto.createCipheriv)("aes-256-gcm", derived, iv);
|
|
260
287
|
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
261
288
|
const authTag = cipher.getAuthTag();
|
|
262
289
|
return `${ALGO_PREFIX}:${salt.toString("base64")}:${iv.toString("base64")}:${authTag.toString("base64")}:${encrypted.toString("base64")}`;
|
|
@@ -276,11 +303,11 @@ function decrypt(ciphertext, key) {
|
|
|
276
303
|
throw new Error("Invalid ciphertext payload");
|
|
277
304
|
}
|
|
278
305
|
const salt = Buffer.from(saltB64, "base64");
|
|
279
|
-
const derived = Buffer.from((0,
|
|
306
|
+
const derived = Buffer.from((0, import_node_crypto.hkdfSync)("sha256", key, salt, "mastra-slack-encryption", 32));
|
|
280
307
|
const iv = Buffer.from(ivB64, "base64");
|
|
281
308
|
const authTag = Buffer.from(authTagB64, "base64");
|
|
282
309
|
const encrypted = Buffer.from(encryptedB64, "base64");
|
|
283
|
-
const decipher = (0,
|
|
310
|
+
const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", derived, iv);
|
|
284
311
|
decipher.setAuthTag(authTag);
|
|
285
312
|
return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString("utf8");
|
|
286
313
|
}
|
|
@@ -14899,7 +14926,9 @@ var SlashCommandSchema = external_exports.object({
|
|
|
14899
14926
|
usageHint: external_exports.string().optional(),
|
|
14900
14927
|
prompt: external_exports.string().optional()
|
|
14901
14928
|
});
|
|
14929
|
+
var OwnerTypeSchema = external_exports.enum(["agent", "agentController"]);
|
|
14902
14930
|
var SlackInstallationDataSchema = external_exports.object({
|
|
14931
|
+
ownerType: OwnerTypeSchema.optional(),
|
|
14903
14932
|
appId: external_exports.string(),
|
|
14904
14933
|
clientId: external_exports.string(),
|
|
14905
14934
|
clientSecret: external_exports.string(),
|
|
@@ -14916,6 +14945,7 @@ var SlackInstallationDataSchema = external_exports.object({
|
|
|
14916
14945
|
slashCommands: external_exports.array(SlashCommandSchema).optional()
|
|
14917
14946
|
});
|
|
14918
14947
|
var SlackPendingDataSchema = external_exports.object({
|
|
14948
|
+
ownerType: OwnerTypeSchema.optional(),
|
|
14919
14949
|
appId: external_exports.string(),
|
|
14920
14950
|
clientId: external_exports.string(),
|
|
14921
14951
|
clientSecret: external_exports.string(),
|
|
@@ -15245,6 +15275,7 @@ var SlackProvider = class {
|
|
|
15245
15275
|
webhookId: installation.webhookId,
|
|
15246
15276
|
configHash: installation.configHash,
|
|
15247
15277
|
data: {
|
|
15278
|
+
ownerType: installation.ownerType,
|
|
15248
15279
|
appId: installation.appId,
|
|
15249
15280
|
clientId: installation.clientId,
|
|
15250
15281
|
clientSecret: installation.clientSecret,
|
|
@@ -15291,6 +15322,7 @@ var SlackProvider = class {
|
|
|
15291
15322
|
webhookId: pending.webhookId,
|
|
15292
15323
|
configHash: pending.configHash,
|
|
15293
15324
|
data: {
|
|
15325
|
+
ownerType: pending.ownerType,
|
|
15294
15326
|
appId: pending.appId,
|
|
15295
15327
|
clientId: pending.clientId,
|
|
15296
15328
|
clientSecret: pending.clientSecret,
|
|
@@ -15418,7 +15450,7 @@ var SlackProvider = class {
|
|
|
15418
15450
|
await this.#checkConfigDrift(installation, baseUrl);
|
|
15419
15451
|
}
|
|
15420
15452
|
console.log(
|
|
15421
|
-
`[Slack] \u2713
|
|
15453
|
+
`[Slack] \u2713 App "${installation.name ?? installation.agentId}" (${installation.appId}) connected to workspace "${installation.teamName ?? installation.teamId}"`
|
|
15422
15454
|
);
|
|
15423
15455
|
} catch (err) {
|
|
15424
15456
|
console.error(`[Slack] Failed to restore installation "${installationEncrypted.id}":`, err);
|
|
@@ -15430,7 +15462,9 @@ var SlackProvider = class {
|
|
|
15430
15462
|
* Creates and injects AgentChannels into the Agent if needed.
|
|
15431
15463
|
*/
|
|
15432
15464
|
async #activateAdapter(installation) {
|
|
15433
|
-
const
|
|
15465
|
+
const isController = installation.ownerType === "agentController";
|
|
15466
|
+
const controller = isController ? this.#resolveAgentController(installation.agentId) : void 0;
|
|
15467
|
+
const agent = isController ? void 0 : this.#resolveAgent(installation.agentId);
|
|
15434
15468
|
const displayName = installation.name || agent?.name || installation.agentId;
|
|
15435
15469
|
const adapter = (0, import_slack.createSlackAdapter)({
|
|
15436
15470
|
...this.#forwardedAdapterOptions(),
|
|
@@ -15443,7 +15477,10 @@ var SlackProvider = class {
|
|
|
15443
15477
|
if (installation.slashCommands?.length) {
|
|
15444
15478
|
this.#slashCommands.set(installation.webhookId, installation.slashCommands);
|
|
15445
15479
|
}
|
|
15446
|
-
if (
|
|
15480
|
+
if (controller && this.#mastra) {
|
|
15481
|
+
const controllerChannels = this.#createAgentControllerChannels(controller, adapter);
|
|
15482
|
+
await controllerChannels.initialize(this.#mastra);
|
|
15483
|
+
} else if (agent && this.#mastra) {
|
|
15447
15484
|
const agentChannels = this.#createAgentChannels(agent, adapter);
|
|
15448
15485
|
await agentChannels.initialize(this.#mastra);
|
|
15449
15486
|
}
|
|
@@ -15453,7 +15490,8 @@ var SlackProvider = class {
|
|
|
15453
15490
|
* If it has, update the Slack app manifest to match the current agent state.
|
|
15454
15491
|
*/
|
|
15455
15492
|
async #checkConfigDrift(installation, baseUrl) {
|
|
15456
|
-
|
|
15493
|
+
if (installation.ownerType === "agentController") return;
|
|
15494
|
+
const agent = this.#resolveAgent(installation.agentId);
|
|
15457
15495
|
if (!agent) return;
|
|
15458
15496
|
const resolvedAppName = installation.name ?? agent.name ?? installation.agentId;
|
|
15459
15497
|
const resolvedDescription = installation.description || agent.getDescription() || "AI assistant powered by Mastra";
|
|
@@ -15481,7 +15519,9 @@ var SlackProvider = class {
|
|
|
15481
15519
|
await this.#requireManifestClient().updateApp(installation.appId, manifest);
|
|
15482
15520
|
const updated = { ...installation, configHash: currentHash };
|
|
15483
15521
|
await this.#saveInstallation(this.#encryptInstallation(updated));
|
|
15484
|
-
console.log(
|
|
15522
|
+
console.log(
|
|
15523
|
+
`[Slack] \u2713 Manifest updated for app "${installation.name ?? installation.agentId}" (${installation.appId})`
|
|
15524
|
+
);
|
|
15485
15525
|
} catch (err) {
|
|
15486
15526
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
15487
15527
|
const isAppGone = errMsg.includes("app_not_found") || errMsg.includes("no_permission");
|
|
@@ -15514,7 +15554,15 @@ var SlackProvider = class {
|
|
|
15514
15554
|
*/
|
|
15515
15555
|
#forwardedChannelOptions() {
|
|
15516
15556
|
const { handlers, inlineMedia, inlineLinks, state, threadContext, tools, chatOptions } = this.#channelConfig;
|
|
15517
|
-
const candidate = {
|
|
15557
|
+
const candidate = {
|
|
15558
|
+
handlers,
|
|
15559
|
+
inlineMedia,
|
|
15560
|
+
inlineLinks,
|
|
15561
|
+
state,
|
|
15562
|
+
threadContext,
|
|
15563
|
+
tools,
|
|
15564
|
+
chatOptions
|
|
15565
|
+
};
|
|
15518
15566
|
return Object.fromEntries(Object.entries(candidate).filter(([, value]) => value !== void 0));
|
|
15519
15567
|
}
|
|
15520
15568
|
/**
|
|
@@ -15579,6 +15627,68 @@ var SlackProvider = class {
|
|
|
15579
15627
|
agent.setChannels(agentChannels);
|
|
15580
15628
|
return agentChannels;
|
|
15581
15629
|
}
|
|
15630
|
+
/**
|
|
15631
|
+
* Controller-parallel of {@link #createAgentChannels}: build a fresh
|
|
15632
|
+
* {@link AgentControllerChannels} that preserves any config the controller
|
|
15633
|
+
* already had (including other adapters) and layers this Slack adapter on,
|
|
15634
|
+
* then swap it onto the controller via `setChannels`. Mirrors the agent path
|
|
15635
|
+
* — a superset merge, not a mutation of the live instance.
|
|
15636
|
+
*
|
|
15637
|
+
* The swap discards the old instance's in-memory `autoApproveResourceIds`
|
|
15638
|
+
* tracking. That's fine: the set is refreshed on every inbound message, and
|
|
15639
|
+
* Slack renders approval buttons anyway so it stays empty here.
|
|
15640
|
+
*/
|
|
15641
|
+
#createAgentControllerChannels(controller, adapter) {
|
|
15642
|
+
const adapterConfig = this.#resolveSlackAdapterConfig();
|
|
15643
|
+
const slackEntry = Object.keys(adapterConfig).length > 0 ? { adapter, ...adapterConfig } : adapter;
|
|
15644
|
+
const existing = controller.getChannels();
|
|
15645
|
+
const existingConfig = existing?.channelConfig;
|
|
15646
|
+
const controllerChannels = new import_channels.AgentControllerChannels({
|
|
15647
|
+
...existingConfig,
|
|
15648
|
+
...this.#forwardedChannelOptions(),
|
|
15649
|
+
adapters: { ...existingConfig?.adapters, slack: slackEntry },
|
|
15650
|
+
userName: controller.id
|
|
15651
|
+
});
|
|
15652
|
+
controller.setChannels(controllerChannels);
|
|
15653
|
+
return controllerChannels;
|
|
15654
|
+
}
|
|
15655
|
+
/**
|
|
15656
|
+
* Resolve the live channels instance that should handle an incoming webhook
|
|
15657
|
+
* for this installation, branching on `ownerType`. Reuses an existing
|
|
15658
|
+
* instance when it already carries the current adapter (e.g. from startup
|
|
15659
|
+
* activation), otherwise builds a fresh one and initializes it. Returns null
|
|
15660
|
+
* when the owner (agent or controller) can't be resolved.
|
|
15661
|
+
*/
|
|
15662
|
+
async #resolveChannelsForInstallation(installation, currentAdapter) {
|
|
15663
|
+
if (!this.#mastra) {
|
|
15664
|
+
console.error("[Slack] Mastra not attached");
|
|
15665
|
+
return null;
|
|
15666
|
+
}
|
|
15667
|
+
if (installation.ownerType === "agentController") {
|
|
15668
|
+
const controller = this.#resolveAgentController(installation.agentId);
|
|
15669
|
+
if (!controller) {
|
|
15670
|
+
console.error(`[Slack] Agent controller "${installation.agentId}" not found`);
|
|
15671
|
+
return null;
|
|
15672
|
+
}
|
|
15673
|
+
let channels2 = controller.getChannels();
|
|
15674
|
+
if (!channels2 || channels2.adapters.slack !== currentAdapter) {
|
|
15675
|
+
channels2 = this.#createAgentControllerChannels(controller, currentAdapter);
|
|
15676
|
+
await channels2.initialize(this.#mastra);
|
|
15677
|
+
}
|
|
15678
|
+
return channels2;
|
|
15679
|
+
}
|
|
15680
|
+
const agent = this.#resolveAgent(installation.agentId);
|
|
15681
|
+
if (!agent) {
|
|
15682
|
+
console.error(`[Slack] Agent "${installation.agentId}" not found`);
|
|
15683
|
+
return null;
|
|
15684
|
+
}
|
|
15685
|
+
let channels = agent.getChannels();
|
|
15686
|
+
if (!channels || channels.adapters.slack !== currentAdapter) {
|
|
15687
|
+
channels = this.#createAgentChannels(agent, currentAdapter);
|
|
15688
|
+
await channels.initialize(this.#mastra);
|
|
15689
|
+
}
|
|
15690
|
+
return channels;
|
|
15691
|
+
}
|
|
15582
15692
|
/**
|
|
15583
15693
|
* Auto-initialize on first route hit.
|
|
15584
15694
|
* Delegates to initialize() which handles idempotency.
|
|
@@ -15642,15 +15752,13 @@ var SlackProvider = class {
|
|
|
15642
15752
|
}
|
|
15643
15753
|
];
|
|
15644
15754
|
}
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
|
|
15650
|
-
|
|
15651
|
-
|
|
15652
|
-
*/
|
|
15653
|
-
async connect(agentId, options) {
|
|
15755
|
+
async connect(agentIdOrOptions, maybeOptions) {
|
|
15756
|
+
const isAgentIdForm = typeof agentIdOrOptions === "string";
|
|
15757
|
+
const agentId = isAgentIdForm ? agentIdOrOptions : agentIdOrOptions.id;
|
|
15758
|
+
const options = isAgentIdForm ? maybeOptions : agentIdOrOptions;
|
|
15759
|
+
if (!isAgentIdForm && !agentIdOrOptions.name) {
|
|
15760
|
+
throw new Error('connect({ id, name }): "name" is required when connecting without an agent id');
|
|
15761
|
+
}
|
|
15654
15762
|
const client = this.#requireManifestClient();
|
|
15655
15763
|
const baseUrl = this.#getBaseUrl();
|
|
15656
15764
|
if (!baseUrl) {
|
|
@@ -15659,26 +15767,45 @@ var SlackProvider = class {
|
|
|
15659
15767
|
);
|
|
15660
15768
|
}
|
|
15661
15769
|
const agent = this.#resolveAgent(agentId);
|
|
15662
|
-
if (!agent) {
|
|
15770
|
+
if (isAgentIdForm && !agent) {
|
|
15663
15771
|
throw new Error(`Agent "${agentId}" not found`);
|
|
15664
15772
|
}
|
|
15773
|
+
let ownerType = "agent";
|
|
15774
|
+
if (!isAgentIdForm && !agent) {
|
|
15775
|
+
const controller = this.#resolveAgentController(agentId);
|
|
15776
|
+
if (!controller) {
|
|
15777
|
+
throw new Error(`No agent or agent controller found with id "${agentId}"`);
|
|
15778
|
+
}
|
|
15779
|
+
ownerType = "agentController";
|
|
15780
|
+
}
|
|
15665
15781
|
const storage = await this.#getStorage();
|
|
15666
15782
|
const existingRecord = await storage.getInstallationByAgent(PLATFORM, agentId);
|
|
15667
15783
|
if (existingRecord?.status === "pending") {
|
|
15668
15784
|
try {
|
|
15669
15785
|
const pending = this.#parsePendingInstallation(existingRecord);
|
|
15670
15786
|
const decrypted = this.#decryptPendingInstallation(pending);
|
|
15671
|
-
|
|
15672
|
-
|
|
15673
|
-
|
|
15674
|
-
|
|
15787
|
+
let appStillExists = true;
|
|
15788
|
+
try {
|
|
15789
|
+
appStillExists = await client.appExists(decrypted.appId);
|
|
15790
|
+
} catch (err) {
|
|
15791
|
+
console.warn(`[Slack] Could not verify existing app for "${agentId}", reusing pending installation:`, err);
|
|
15792
|
+
}
|
|
15793
|
+
if (!appStillExists) {
|
|
15794
|
+
console.warn(`[Slack] Stored app for "${agentId}" no longer exists on Slack, creating a fresh installation`);
|
|
15795
|
+
await storage.deleteInstallation(existingRecord.id);
|
|
15796
|
+
} else {
|
|
15797
|
+
if (options?.redirectUrl && decrypted.redirectUrl !== options.redirectUrl) {
|
|
15798
|
+
await this.#savePendingInstallation(
|
|
15799
|
+
this.#encryptPendingInstallation({ ...decrypted, redirectUrl: options.redirectUrl })
|
|
15800
|
+
);
|
|
15801
|
+
}
|
|
15802
|
+
console.log(`[Slack] Reusing existing pending installation for "${agentId}"`);
|
|
15803
|
+
return {
|
|
15804
|
+
type: "oauth",
|
|
15805
|
+
installationId: decrypted.id,
|
|
15806
|
+
authorizationUrl: decrypted.authorizationUrl
|
|
15807
|
+
};
|
|
15675
15808
|
}
|
|
15676
|
-
console.log(`[Slack] Reusing existing pending installation for "${agentId}"`);
|
|
15677
|
-
return {
|
|
15678
|
-
type: "oauth",
|
|
15679
|
-
installationId: decrypted.id,
|
|
15680
|
-
authorizationUrl: decrypted.authorizationUrl
|
|
15681
|
-
};
|
|
15682
15809
|
} catch {
|
|
15683
15810
|
console.warn(`[Slack] Corrupt pending installation for "${agentId}", replacing it`);
|
|
15684
15811
|
await storage.deleteInstallation(existingRecord.id);
|
|
@@ -15689,8 +15816,8 @@ var SlackProvider = class {
|
|
|
15689
15816
|
}
|
|
15690
15817
|
const config2 = options ?? {};
|
|
15691
15818
|
const webhookId = crypto.randomUUID();
|
|
15692
|
-
const appName = config2.name ?? agent
|
|
15693
|
-
const appDescription = config2.description || agent
|
|
15819
|
+
const appName = config2.name ?? agent?.name ?? agentId;
|
|
15820
|
+
const appDescription = config2.description || agent?.getDescription() || "AI assistant powered by Mastra";
|
|
15694
15821
|
const normalizedCommands = this.#normalizeCommands(config2.slashCommands);
|
|
15695
15822
|
let manifest = buildManifest({
|
|
15696
15823
|
name: appName,
|
|
@@ -15733,6 +15860,7 @@ var SlackProvider = class {
|
|
|
15733
15860
|
const pendingInstallation = this.#encryptPendingInstallation({
|
|
15734
15861
|
id: installationId,
|
|
15735
15862
|
agentId,
|
|
15863
|
+
ownerType,
|
|
15736
15864
|
webhookId,
|
|
15737
15865
|
appId: appCredentials.appId,
|
|
15738
15866
|
clientId: appCredentials.clientId,
|
|
@@ -15941,6 +16069,7 @@ var SlackProvider = class {
|
|
|
15941
16069
|
const installation = {
|
|
15942
16070
|
id: pending.id,
|
|
15943
16071
|
agentId: pending.agentId,
|
|
16072
|
+
ownerType: pending.ownerType ?? "agent",
|
|
15944
16073
|
webhookId: pending.webhookId,
|
|
15945
16074
|
appId: pending.appId,
|
|
15946
16075
|
clientId: pending.clientId,
|
|
@@ -15958,7 +16087,11 @@ var SlackProvider = class {
|
|
|
15958
16087
|
};
|
|
15959
16088
|
const encryptedInstallation = this.#encryptInstallation(installation);
|
|
15960
16089
|
await this.#saveInstallation(encryptedInstallation);
|
|
15961
|
-
|
|
16090
|
+
let agent;
|
|
16091
|
+
try {
|
|
16092
|
+
agent = installation.ownerType === "agent" ? this.#mastra?.getAgentById(pending.agentId) : void 0;
|
|
16093
|
+
} catch {
|
|
16094
|
+
}
|
|
15962
16095
|
const displayName = installation.name || agent?.name || pending.agentId;
|
|
15963
16096
|
const adapter = (0, import_slack.createSlackAdapter)({
|
|
15964
16097
|
...this.#forwardedAdapterOptions(),
|
|
@@ -15975,7 +16108,9 @@ var SlackProvider = class {
|
|
|
15975
16108
|
await this.#channelConfig.onInstall(installation);
|
|
15976
16109
|
}
|
|
15977
16110
|
const teamName = tokenData.team?.name ?? "";
|
|
15978
|
-
console.log(
|
|
16111
|
+
console.log(
|
|
16112
|
+
`[Slack] \u2713 App "${installation.name ?? installation.agentId}" (${installation.appId}) installed to workspace "${teamName || installation.teamId}"`
|
|
16113
|
+
);
|
|
15979
16114
|
const successUrl = pending.redirectUrl ?? this.#channelConfig.redirectPath ?? "/";
|
|
15980
16115
|
const redirect = new URL(successUrl, c.req.url);
|
|
15981
16116
|
redirect.searchParams.set("channel_connected", "true");
|
|
@@ -16030,16 +16165,11 @@ var SlackProvider = class {
|
|
|
16030
16165
|
return c.json({ challenge: event.challenge });
|
|
16031
16166
|
}
|
|
16032
16167
|
}
|
|
16033
|
-
const agent = this.#resolveAgent(installation.agentId);
|
|
16034
|
-
if (!agent) {
|
|
16035
|
-
console.error(`[Slack] Agent "${installation.agentId}" not found`);
|
|
16036
|
-
return c.json({ ok: true });
|
|
16037
|
-
}
|
|
16038
16168
|
if (!this.#mastra) {
|
|
16039
16169
|
console.error("[Slack] Mastra not attached");
|
|
16040
16170
|
return c.json({ ok: true });
|
|
16041
16171
|
}
|
|
16042
|
-
const displayName = installation.name ||
|
|
16172
|
+
const displayName = installation.name || installation.agentId;
|
|
16043
16173
|
const currentAdapter = this.#adapters.get(installation.id) ?? (0, import_slack.createSlackAdapter)({
|
|
16044
16174
|
...this.#forwardedAdapterOptions(),
|
|
16045
16175
|
botToken: installation.botToken,
|
|
@@ -16047,10 +16177,9 @@ var SlackProvider = class {
|
|
|
16047
16177
|
signingSecret: installation.signingSecret,
|
|
16048
16178
|
userName: displayName
|
|
16049
16179
|
});
|
|
16050
|
-
|
|
16051
|
-
if (!agentChannels
|
|
16052
|
-
|
|
16053
|
-
await agentChannels.initialize(this.#mastra);
|
|
16180
|
+
const agentChannels = await this.#resolveChannelsForInstallation(installation, currentAdapter);
|
|
16181
|
+
if (!agentChannels) {
|
|
16182
|
+
return c.json({ ok: true });
|
|
16054
16183
|
}
|
|
16055
16184
|
const delegateRequest = new Request(c.req.url, {
|
|
16056
16185
|
method: c.req.method,
|
|
@@ -16101,6 +16230,12 @@ var SlackProvider = class {
|
|
|
16101
16230
|
if (!commandConfig) {
|
|
16102
16231
|
return c.json({ response_type: "ephemeral", text: `Unknown command: ${command}` });
|
|
16103
16232
|
}
|
|
16233
|
+
if (installation.ownerType === "agentController") {
|
|
16234
|
+
return c.json({
|
|
16235
|
+
response_type: "ephemeral",
|
|
16236
|
+
text: "Slash commands are not supported for this app yet. Mention the bot in a thread to start a session."
|
|
16237
|
+
});
|
|
16238
|
+
}
|
|
16104
16239
|
const agent = this.#resolveAgent(installation.agentId);
|
|
16105
16240
|
if (!agent) {
|
|
16106
16241
|
return c.json({ response_type: "ephemeral", text: "Agent not available" });
|
|
@@ -16142,6 +16277,13 @@ var SlackProvider = class {
|
|
|
16142
16277
|
return void 0;
|
|
16143
16278
|
}
|
|
16144
16279
|
}
|
|
16280
|
+
#resolveAgentController(controllerId) {
|
|
16281
|
+
try {
|
|
16282
|
+
return this.#mastra?.getAgentControllerById(controllerId);
|
|
16283
|
+
} catch {
|
|
16284
|
+
return void 0;
|
|
16285
|
+
}
|
|
16286
|
+
}
|
|
16145
16287
|
};
|
|
16146
16288
|
|
|
16147
16289
|
// src/index.ts
|