@spinabot/brigade 1.5.0 → 1.6.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/agents/channels/bundled-channel-metas.d.ts +2 -0
- package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -1
- package/dist/agents/channels/bundled-channel-metas.js +11 -0
- package/dist/agents/channels/bundled-channel-metas.js.map +1 -1
- package/dist/agents/channels/manager.d.ts.map +1 -1
- package/dist/agents/channels/manager.js +18 -0
- package/dist/agents/channels/manager.js.map +1 -1
- package/dist/agents/channels/sdk.d.ts +2 -0
- package/dist/agents/channels/sdk.d.ts.map +1 -1
- package/dist/agents/channels/sdk.js +2 -0
- package/dist/agents/channels/sdk.js.map +1 -1
- package/dist/agents/channels/slack/account-config.d.ts +172 -0
- package/dist/agents/channels/slack/account-config.d.ts.map +1 -0
- package/dist/agents/channels/slack/account-config.js +353 -0
- package/dist/agents/channels/slack/account-config.js.map +1 -0
- package/dist/agents/channels/slack/account-registry.d.ts +45 -0
- package/dist/agents/channels/slack/account-registry.d.ts.map +1 -0
- package/dist/agents/channels/slack/account-registry.js +58 -0
- package/dist/agents/channels/slack/account-registry.js.map +1 -0
- package/dist/agents/channels/slack/adapter.d.ts +66 -0
- package/dist/agents/channels/slack/adapter.d.ts.map +1 -0
- package/dist/agents/channels/slack/adapter.js +547 -0
- package/dist/agents/channels/slack/adapter.js.map +1 -0
- package/dist/agents/channels/slack/approval-authorize.d.ts +43 -0
- package/dist/agents/channels/slack/approval-authorize.d.ts.map +1 -0
- package/dist/agents/channels/slack/approval-authorize.js +71 -0
- package/dist/agents/channels/slack/approval-authorize.js.map +1 -0
- package/dist/agents/channels/slack/approval-native.d.ts +70 -0
- package/dist/agents/channels/slack/approval-native.d.ts.map +1 -0
- package/dist/agents/channels/slack/approval-native.js +85 -0
- package/dist/agents/channels/slack/approval-native.js.map +1 -0
- package/dist/agents/channels/slack/blocks.d.ts +125 -0
- package/dist/agents/channels/slack/blocks.d.ts.map +1 -0
- package/dist/agents/channels/slack/blocks.js +145 -0
- package/dist/agents/channels/slack/blocks.js.map +1 -0
- package/dist/agents/channels/slack/command-menu.d.ts +44 -0
- package/dist/agents/channels/slack/command-menu.d.ts.map +1 -0
- package/dist/agents/channels/slack/command-menu.js +66 -0
- package/dist/agents/channels/slack/command-menu.js.map +1 -0
- package/dist/agents/channels/slack/connection.d.ts +422 -0
- package/dist/agents/channels/slack/connection.d.ts.map +1 -0
- package/dist/agents/channels/slack/connection.js +1042 -0
- package/dist/agents/channels/slack/connection.js.map +1 -0
- package/dist/agents/channels/slack/directory-live.d.ts +129 -0
- package/dist/agents/channels/slack/directory-live.d.ts.map +1 -0
- package/dist/agents/channels/slack/directory-live.js +148 -0
- package/dist/agents/channels/slack/directory-live.js.map +1 -0
- package/dist/agents/channels/slack/draft-stream.d.ts +93 -0
- package/dist/agents/channels/slack/draft-stream.d.ts.map +1 -0
- package/dist/agents/channels/slack/draft-stream.js +218 -0
- package/dist/agents/channels/slack/draft-stream.js.map +1 -0
- package/dist/agents/channels/slack/format.d.ts +41 -0
- package/dist/agents/channels/slack/format.d.ts.map +1 -0
- package/dist/agents/channels/slack/format.js +271 -0
- package/dist/agents/channels/slack/format.js.map +1 -0
- package/dist/agents/channels/slack/inbound-extras.d.ts +179 -0
- package/dist/agents/channels/slack/inbound-extras.d.ts.map +1 -0
- package/dist/agents/channels/slack/inbound-extras.js +257 -0
- package/dist/agents/channels/slack/inbound-extras.js.map +1 -0
- package/dist/agents/channels/slack/index.d.ts +15 -0
- package/dist/agents/channels/slack/index.d.ts.map +1 -0
- package/dist/agents/channels/slack/index.js +15 -0
- package/dist/agents/channels/slack/index.js.map +1 -0
- package/dist/agents/channels/slack/media.d.ts +90 -0
- package/dist/agents/channels/slack/media.d.ts.map +1 -0
- package/dist/agents/channels/slack/media.js +215 -0
- package/dist/agents/channels/slack/media.js.map +1 -0
- package/dist/agents/channels/slack/module.d.ts +26 -0
- package/dist/agents/channels/slack/module.d.ts.map +1 -0
- package/dist/agents/channels/slack/module.js +67 -0
- package/dist/agents/channels/slack/module.js.map +1 -0
- package/dist/agents/channels/slack/plugin.d.ts +69 -0
- package/dist/agents/channels/slack/plugin.d.ts.map +1 -0
- package/dist/agents/channels/slack/plugin.js +318 -0
- package/dist/agents/channels/slack/plugin.js.map +1 -0
- package/dist/agents/channels/slack/probe.d.ts +72 -0
- package/dist/agents/channels/slack/probe.d.ts.map +1 -0
- package/dist/agents/channels/slack/probe.js +103 -0
- package/dist/agents/channels/slack/probe.js.map +1 -0
- package/dist/agents/channels/slack/proxy-agent.d.ts +30 -0
- package/dist/agents/channels/slack/proxy-agent.d.ts.map +1 -0
- package/dist/agents/channels/slack/proxy-agent.js +44 -0
- package/dist/agents/channels/slack/proxy-agent.js.map +1 -0
- package/dist/agents/channels/slack/reasoning-lane.d.ts +42 -0
- package/dist/agents/channels/slack/reasoning-lane.d.ts.map +1 -0
- package/dist/agents/channels/slack/reasoning-lane.js +68 -0
- package/dist/agents/channels/slack/reasoning-lane.js.map +1 -0
- package/dist/agents/channels/slack/user-directory.d.ts +69 -0
- package/dist/agents/channels/slack/user-directory.d.ts.map +1 -0
- package/dist/agents/channels/slack/user-directory.js +94 -0
- package/dist/agents/channels/slack/user-directory.js.map +1 -0
- package/dist/agents/channels/slack/webhook.d.ts +89 -0
- package/dist/agents/channels/slack/webhook.d.ts.map +1 -0
- package/dist/agents/channels/slack/webhook.js +228 -0
- package/dist/agents/channels/slack/webhook.js.map +1 -0
- package/dist/agents/channels/telegram/format.d.ts.map +1 -1
- package/dist/agents/channels/telegram/format.js +17 -1
- package/dist/agents/channels/telegram/format.js.map +1 -1
- package/dist/agents/channels/telegram/webhook.d.ts.map +1 -1
- package/dist/agents/channels/telegram/webhook.js +7 -1
- package/dist/agents/channels/telegram/webhook.js.map +1 -1
- package/dist/agents/extensions/modules/index.d.ts.map +1 -1
- package/dist/agents/extensions/modules/index.js +5 -0
- package/dist/agents/extensions/modules/index.js.map +1 -1
- package/dist/buildstamp.json +1 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +24 -5
- package/dist/core/server.js.map +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure extractors that turn a Slack Events-API event payload into the
|
|
3
|
+
* normalized fields Brigade's `InboundMessage` carries. No network, no side
|
|
4
|
+
* effects — every function here is deterministic over its event argument so
|
|
5
|
+
* they're trivial to unit-test without a live workspace.
|
|
6
|
+
*
|
|
7
|
+
* Slack's wire shape differs from Telegram's in load-bearing ways, so the logic
|
|
8
|
+
* is a Brigade-native re-implementation that models the SHAPE of
|
|
9
|
+
* `telegram/inbound-extras.ts` (raw event → normalized signals) while speaking
|
|
10
|
+
* Slack semantics:
|
|
11
|
+
*
|
|
12
|
+
* - Text arrives as `event.text` peppered with Slack mention/link TOKENS:
|
|
13
|
+
* `<@U123>` (user), `<#C123|name>` (channel), `<https://x|label>` (link),
|
|
14
|
+
* `<!here>` / `<!subteam^S1|@team>` (special). {@link extractSlackText}
|
|
15
|
+
* expands those into readable plain text the agent can parse.
|
|
16
|
+
* - The bot is "addressed" when its own `user_id` appears as a `<@Uxxx>`
|
|
17
|
+
* mention (or the event is an `app_mention`). {@link extractSlackMentions}
|
|
18
|
+
* surfaces the bot's id when addressed so the central group ACL admits the
|
|
19
|
+
* message — exactly as Telegram surfaces the bot's numeric id.
|
|
20
|
+
* - Channel kind is read from `event.channel_type` (`im` → direct;
|
|
21
|
+
* `mpim`/`channel`/`group` → group), see {@link slackChannelType}.
|
|
22
|
+
* - Threads ride on `thread_ts`; a reply quotes its parent.
|
|
23
|
+
* - Files arrive as `event.files[]` (each a `url_private` + metadata); the
|
|
24
|
+
* connection layer DEFERS the byte download until the access gate admits
|
|
25
|
+
* the sender (mirrors Telegram's deferred-media discipline).
|
|
26
|
+
*
|
|
27
|
+
* Brigade's CENTRAL inbound pipeline owns the actual ACL / mention / routing
|
|
28
|
+
* decision — these helpers only surface the raw signals it reads (`mentions`,
|
|
29
|
+
* `threadId`, `replyTo`, chat type).
|
|
30
|
+
*/
|
|
31
|
+
import type { InboundReplyContext } from "../sdk.js";
|
|
32
|
+
/** One file object Slack attaches to a message event (the subset we read). */
|
|
33
|
+
export interface SlackFileObject {
|
|
34
|
+
id?: string;
|
|
35
|
+
name?: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
mimetype?: string;
|
|
38
|
+
filetype?: string;
|
|
39
|
+
/** Authenticated download URL — requires `Authorization: Bearer <botToken>`. */
|
|
40
|
+
url_private?: string;
|
|
41
|
+
url_private_download?: string;
|
|
42
|
+
size?: number;
|
|
43
|
+
/** Slack marks a file `mode: "tombstone"` when it was deleted / is unavailable. */
|
|
44
|
+
mode?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A Slack message event (the `message` / `app_mention` family). Only the fields
|
|
48
|
+
* Brigade consumes are typed; the raw event carries far more. `subtype`
|
|
49
|
+
* discriminates edits (`message_changed`), deletes (`message_deleted`), and the
|
|
50
|
+
* many bot/system message variants we filter out.
|
|
51
|
+
*/
|
|
52
|
+
export interface SlackMessageEvent {
|
|
53
|
+
type?: string;
|
|
54
|
+
subtype?: string;
|
|
55
|
+
/** Sender user id (`U…` / `W…`). Absent on some system subtypes. */
|
|
56
|
+
user?: string;
|
|
57
|
+
/** Bot id when the message was posted by a bot integration (not a user). */
|
|
58
|
+
bot_id?: string;
|
|
59
|
+
text?: string;
|
|
60
|
+
/** Channel id (`C…` public, `G…` private, `D…` DM). */
|
|
61
|
+
channel?: string;
|
|
62
|
+
/** `im` (DM) | `mpim` (group DM) | `channel` (public) | `group` (private). */
|
|
63
|
+
channel_type?: string;
|
|
64
|
+
/** Message timestamp — Slack's per-message id within a channel. */
|
|
65
|
+
ts?: string;
|
|
66
|
+
/** Parent thread ts when this message belongs to a thread. */
|
|
67
|
+
thread_ts?: string;
|
|
68
|
+
/** Client-generated id — a stable dedupe key across redeliveries. */
|
|
69
|
+
client_msg_id?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Edit stamp Slack sets on an edited message (`{ user, ts }` where `ts` is
|
|
72
|
+
* WHEN the edit happened, distinct from the message's own `ts`). Folded into
|
|
73
|
+
* the dedupe key so a SECOND edit of the same message isn't dropped.
|
|
74
|
+
*/
|
|
75
|
+
edited?: {
|
|
76
|
+
ts?: string;
|
|
77
|
+
user?: string;
|
|
78
|
+
};
|
|
79
|
+
/** Files attached to the message. */
|
|
80
|
+
files?: SlackFileObject[];
|
|
81
|
+
/** The edited / changed message envelope (subtype `message_changed`). */
|
|
82
|
+
message?: SlackMessageEvent;
|
|
83
|
+
/** The prior message before an edit (subtype `message_changed`). */
|
|
84
|
+
previous_message?: SlackMessageEvent;
|
|
85
|
+
/** The deleted message's ts (subtype `message_deleted`). */
|
|
86
|
+
deleted_ts?: string;
|
|
87
|
+
[key: string]: unknown;
|
|
88
|
+
}
|
|
89
|
+
/** A Slack `reaction_added` / `reaction_removed` event (the subset we read). */
|
|
90
|
+
export interface SlackReactionEvent {
|
|
91
|
+
type?: string;
|
|
92
|
+
/** The user who added / removed the reaction. */
|
|
93
|
+
user?: string;
|
|
94
|
+
/** The emoji name (no colons), e.g. `thumbsup`. */
|
|
95
|
+
reaction?: string;
|
|
96
|
+
/** The message the reaction landed on. */
|
|
97
|
+
item?: {
|
|
98
|
+
type?: string;
|
|
99
|
+
channel?: string;
|
|
100
|
+
ts?: string;
|
|
101
|
+
};
|
|
102
|
+
[key: string]: unknown;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Reverse Slack's text-node entity escaping (`&` `<` `>`). Slack
|
|
106
|
+
* escapes only those three in message text; everything else is literal. Applied
|
|
107
|
+
* AFTER token expansion so the angle-bracket tokens (`<…>`) parse first.
|
|
108
|
+
*/
|
|
109
|
+
export declare function unescapeSlackEntities(text: string): string;
|
|
110
|
+
/**
|
|
111
|
+
* Expand the Slack message TOKENS in a run of text into readable plain text:
|
|
112
|
+
* - `<@U123>` / `<@U123|alex>` → `@alex` (or `@U123` when no label)
|
|
113
|
+
* - `<#C123|general>` / `<#C123>` → `#general` (or `#C123`)
|
|
114
|
+
* - `<!here>` / `<!channel>` / `<!everyone>` → `@here` / `@channel` / `@everyone`
|
|
115
|
+
* - `<!subteam^S1|@team>` → `@team`
|
|
116
|
+
* - `<!date^…^fallback|link>` → the fallback text
|
|
117
|
+
* - `<https://x|label>` / `<https://x>` → `label` (or the bare url)
|
|
118
|
+
*
|
|
119
|
+
* Unknown `<…>` tokens collapse to their inner display text (after `|`) or are
|
|
120
|
+
* stripped of the angle brackets. Pure + deterministic.
|
|
121
|
+
*/
|
|
122
|
+
export declare function expandSlackTokens(text: string, resolveName?: (id: string) => string | undefined): string;
|
|
123
|
+
/**
|
|
124
|
+
* The agent-facing plain text of a Slack message. Token-expanded (mentions /
|
|
125
|
+
* channels / links → readable text) then entity-unescaped. A `message_changed`
|
|
126
|
+
* envelope surfaces the EDITED text (`event.message.text`). Binary blobs are
|
|
127
|
+
* dropped to "".
|
|
128
|
+
*/
|
|
129
|
+
export declare function extractSlackText(event: SlackMessageEvent, resolveName?: (id: string) => string | undefined): string;
|
|
130
|
+
/** Slack `channel_type` → Brigade chat type. `im` → direct; everything else → group. */
|
|
131
|
+
export declare function slackChannelType(event: Pick<SlackMessageEvent, "channel_type" | "channel">): "direct" | "group";
|
|
132
|
+
/** Thread parent ts as a string, when the message belongs to a thread. */
|
|
133
|
+
export declare function slackThreadId(event: Pick<SlackMessageEvent, "thread_ts">): string | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* Channel-native ids of accounts addressed in this message. Brigade's central
|
|
136
|
+
* group ACL treats a group message as "addressed to the bot" when the bot's own
|
|
137
|
+
* id appears in `mentions`; without this a group message never reaches the
|
|
138
|
+
* agent. So when the bot's own `<@Uxxx>` mention appears in the text — OR the
|
|
139
|
+
* event is an `app_mention` (Slack's dedicated "the bot was @-mentioned" event)
|
|
140
|
+
* — we surface the bot's user id (passed in from `auth.test`). Every OTHER
|
|
141
|
+
* `<@Uxxx>` user mention is surfaced too so the pipeline sees who else was
|
|
142
|
+
* tagged.
|
|
143
|
+
*
|
|
144
|
+
* @param botUserId the bot's own user id (from `auth.test`), surfaced when the
|
|
145
|
+
* bot is @-mentioned so the ACL admits the group message.
|
|
146
|
+
*/
|
|
147
|
+
export declare function extractSlackMentions(event: SlackMessageEvent, botUserId?: string): string[];
|
|
148
|
+
/**
|
|
149
|
+
* A short display name for the sender. A legacy bot-posted `username` wins; else
|
|
150
|
+
* a resolved display name (when a `resolveName` reader from the user directory is
|
|
151
|
+
* supplied AND it has already cached the sender's name); else the raw user id
|
|
152
|
+
* (`U…`) — display-name resolution needs a `users.info` call that the directory
|
|
153
|
+
* primes in the background, so the first message from a never-seen user surfaces
|
|
154
|
+
* the id and later messages surface the name.
|
|
155
|
+
*/
|
|
156
|
+
export declare function buildSlackSenderName(event: SlackMessageEvent, resolveName?: (id: string) => string | undefined): string | undefined;
|
|
157
|
+
/**
|
|
158
|
+
* Reply-context (what message this inbound quotes), when it's a threaded reply.
|
|
159
|
+
* Slack threads are flat — a reply carries `thread_ts` (the parent's ts) but the
|
|
160
|
+
* event does NOT inline the parent's text — so the context surfaces the parent
|
|
161
|
+
* message id (`thread_ts`) and leaves `body` undefined (the pipeline can fetch
|
|
162
|
+
* the parent if it needs the excerpt). Returns undefined for a top-level
|
|
163
|
+
* message (no `thread_ts`, or `thread_ts === ts` which is the thread ROOT, not a
|
|
164
|
+
* reply).
|
|
165
|
+
*/
|
|
166
|
+
export declare function extractSlackReplyContext(event: SlackMessageEvent): InboundReplyContext | undefined;
|
|
167
|
+
/**
|
|
168
|
+
* Cheap presence probe — does this message carry a downloadable file? Walks
|
|
169
|
+
* `event.files[]` (an edit reads the nested message's files) but never touches
|
|
170
|
+
* the network, so the connection layer can DEFER the actual download until AFTER
|
|
171
|
+
* the central access gate admits the sender (mirrors Telegram's deferred-media
|
|
172
|
+
* discipline). Mirrors `hasInboundMedia`.
|
|
173
|
+
*/
|
|
174
|
+
export declare function hasInboundMedia(event: SlackMessageEvent): boolean;
|
|
175
|
+
/** The list of downloadable files on a message (an edit reads the nested message). */
|
|
176
|
+
export declare function resolveInboundFiles(event: SlackMessageEvent): SlackFileObject[];
|
|
177
|
+
/** The Brigade media-kind of a Slack file, from its mimetype / filetype. */
|
|
178
|
+
export declare function resolveSlackFileKind(f: SlackFileObject): "image" | "video" | "audio" | "voice" | "document";
|
|
179
|
+
//# sourceMappingURL=inbound-extras.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbound-extras.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/slack/inbound-extras.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure extractors that turn a Slack Events-API event payload into the
|
|
3
|
+
* normalized fields Brigade's `InboundMessage` carries. No network, no side
|
|
4
|
+
* effects — every function here is deterministic over its event argument so
|
|
5
|
+
* they're trivial to unit-test without a live workspace.
|
|
6
|
+
*
|
|
7
|
+
* Slack's wire shape differs from Telegram's in load-bearing ways, so the logic
|
|
8
|
+
* is a Brigade-native re-implementation that models the SHAPE of
|
|
9
|
+
* `telegram/inbound-extras.ts` (raw event → normalized signals) while speaking
|
|
10
|
+
* Slack semantics:
|
|
11
|
+
*
|
|
12
|
+
* - Text arrives as `event.text` peppered with Slack mention/link TOKENS:
|
|
13
|
+
* `<@U123>` (user), `<#C123|name>` (channel), `<https://x|label>` (link),
|
|
14
|
+
* `<!here>` / `<!subteam^S1|@team>` (special). {@link extractSlackText}
|
|
15
|
+
* expands those into readable plain text the agent can parse.
|
|
16
|
+
* - The bot is "addressed" when its own `user_id` appears as a `<@Uxxx>`
|
|
17
|
+
* mention (or the event is an `app_mention`). {@link extractSlackMentions}
|
|
18
|
+
* surfaces the bot's id when addressed so the central group ACL admits the
|
|
19
|
+
* message — exactly as Telegram surfaces the bot's numeric id.
|
|
20
|
+
* - Channel kind is read from `event.channel_type` (`im` → direct;
|
|
21
|
+
* `mpim`/`channel`/`group` → group), see {@link slackChannelType}.
|
|
22
|
+
* - Threads ride on `thread_ts`; a reply quotes its parent.
|
|
23
|
+
* - Files arrive as `event.files[]` (each a `url_private` + metadata); the
|
|
24
|
+
* connection layer DEFERS the byte download until the access gate admits
|
|
25
|
+
* the sender (mirrors Telegram's deferred-media discipline).
|
|
26
|
+
*
|
|
27
|
+
* Brigade's CENTRAL inbound pipeline owns the actual ACL / mention / routing
|
|
28
|
+
* decision — these helpers only surface the raw signals it reads (`mentions`,
|
|
29
|
+
* `threadId`, `replyTo`, chat type).
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Reject control-byte payloads (a binary blob masquerading as text). Tab / LF /
|
|
33
|
+
* CR are allowed; any other C0 control char marks the run as non-text and it's
|
|
34
|
+
* dropped so the agent never ingests raw binary. Mirrors Telegram's
|
|
35
|
+
* `isBinaryContent`.
|
|
36
|
+
*/
|
|
37
|
+
function isBinaryContent(text) {
|
|
38
|
+
for (let i = 0; i < text.length; i++) {
|
|
39
|
+
const code = text.charCodeAt(i);
|
|
40
|
+
if (code <= 0x1f && code !== 0x09 && code !== 0x0a && code !== 0x0d) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Reverse Slack's text-node entity escaping (`&` `<` `>`). Slack
|
|
48
|
+
* escapes only those three in message text; everything else is literal. Applied
|
|
49
|
+
* AFTER token expansion so the angle-bracket tokens (`<…>`) parse first.
|
|
50
|
+
*/
|
|
51
|
+
export function unescapeSlackEntities(text) {
|
|
52
|
+
return text.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Expand the Slack message TOKENS in a run of text into readable plain text:
|
|
56
|
+
* - `<@U123>` / `<@U123|alex>` → `@alex` (or `@U123` when no label)
|
|
57
|
+
* - `<#C123|general>` / `<#C123>` → `#general` (or `#C123`)
|
|
58
|
+
* - `<!here>` / `<!channel>` / `<!everyone>` → `@here` / `@channel` / `@everyone`
|
|
59
|
+
* - `<!subteam^S1|@team>` → `@team`
|
|
60
|
+
* - `<!date^…^fallback|link>` → the fallback text
|
|
61
|
+
* - `<https://x|label>` / `<https://x>` → `label` (or the bare url)
|
|
62
|
+
*
|
|
63
|
+
* Unknown `<…>` tokens collapse to their inner display text (after `|`) or are
|
|
64
|
+
* stripped of the angle brackets. Pure + deterministic.
|
|
65
|
+
*/
|
|
66
|
+
export function expandSlackTokens(text, resolveName) {
|
|
67
|
+
return text.replace(/<([^>]*)>/g, (_whole, inner) => {
|
|
68
|
+
if (!inner)
|
|
69
|
+
return "";
|
|
70
|
+
const bar = inner.indexOf("|");
|
|
71
|
+
const head = bar === -1 ? inner : inner.slice(0, bar);
|
|
72
|
+
const label = bar === -1 ? "" : inner.slice(bar + 1);
|
|
73
|
+
const first = head[0];
|
|
74
|
+
// User mention: <@U123> / <@U123|alex>
|
|
75
|
+
if (first === "@") {
|
|
76
|
+
const id = head.slice(1);
|
|
77
|
+
// Inline label wins; else a resolved display name (from the user
|
|
78
|
+
// directory, when one is supplied); else the bare id.
|
|
79
|
+
if (label)
|
|
80
|
+
return `@${label}`;
|
|
81
|
+
const resolved = resolveName?.(id);
|
|
82
|
+
return resolved ? `@${resolved}` : `@${id}`;
|
|
83
|
+
}
|
|
84
|
+
// Channel mention: <#C123|general> / <#C123>
|
|
85
|
+
if (first === "#") {
|
|
86
|
+
const id = head.slice(1);
|
|
87
|
+
return label ? `#${label}` : `#${id}`;
|
|
88
|
+
}
|
|
89
|
+
// Special mention / subteam / date: <!here> / <!subteam^S1|@team> / <!date^…|link>
|
|
90
|
+
if (first === "!") {
|
|
91
|
+
const body = head.slice(1);
|
|
92
|
+
if (label)
|
|
93
|
+
return label.startsWith("@") ? label : `@${label}`;
|
|
94
|
+
// <!here> / <!channel> / <!everyone> → @here etc.; <!subteam^S1> → @team-ish
|
|
95
|
+
const name = body.split("^")[0] ?? body;
|
|
96
|
+
return `@${name}`;
|
|
97
|
+
}
|
|
98
|
+
// Link: <https://x|label> / <mailto:a@b|a@b> / <https://x>
|
|
99
|
+
return label || head;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The agent-facing plain text of a Slack message. Token-expanded (mentions /
|
|
104
|
+
* channels / links → readable text) then entity-unescaped. A `message_changed`
|
|
105
|
+
* envelope surfaces the EDITED text (`event.message.text`). Binary blobs are
|
|
106
|
+
* dropped to "".
|
|
107
|
+
*/
|
|
108
|
+
export function extractSlackText(event, resolveName) {
|
|
109
|
+
// An edit (message_changed) carries the new text on the nested `message`.
|
|
110
|
+
const source = event.subtype === "message_changed" && event.message ? event.message : event;
|
|
111
|
+
const raw = typeof source.text === "string" ? source.text : "";
|
|
112
|
+
if (!raw || isBinaryContent(raw))
|
|
113
|
+
return "";
|
|
114
|
+
return unescapeSlackEntities(expandSlackTokens(raw, resolveName)).trim();
|
|
115
|
+
}
|
|
116
|
+
/** Slack `channel_type` → Brigade chat type. `im` → direct; everything else → group. */
|
|
117
|
+
export function slackChannelType(event) {
|
|
118
|
+
const t = typeof event.channel_type === "string" ? event.channel_type : "";
|
|
119
|
+
if (t === "im")
|
|
120
|
+
return "direct";
|
|
121
|
+
if (t === "mpim" || t === "channel" || t === "group")
|
|
122
|
+
return "group";
|
|
123
|
+
// Fall back to the channel-id prefix when `channel_type` was omitted: a `D…`
|
|
124
|
+
// id is a DM, anything else is a multi-party room.
|
|
125
|
+
const ch = typeof event.channel === "string" ? event.channel : "";
|
|
126
|
+
return ch.startsWith("D") ? "direct" : "group";
|
|
127
|
+
}
|
|
128
|
+
/** Thread parent ts as a string, when the message belongs to a thread. */
|
|
129
|
+
export function slackThreadId(event) {
|
|
130
|
+
const ts = event.thread_ts;
|
|
131
|
+
return typeof ts === "string" && ts ? ts : undefined;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Channel-native ids of accounts addressed in this message. Brigade's central
|
|
135
|
+
* group ACL treats a group message as "addressed to the bot" when the bot's own
|
|
136
|
+
* id appears in `mentions`; without this a group message never reaches the
|
|
137
|
+
* agent. So when the bot's own `<@Uxxx>` mention appears in the text — OR the
|
|
138
|
+
* event is an `app_mention` (Slack's dedicated "the bot was @-mentioned" event)
|
|
139
|
+
* — we surface the bot's user id (passed in from `auth.test`). Every OTHER
|
|
140
|
+
* `<@Uxxx>` user mention is surfaced too so the pipeline sees who else was
|
|
141
|
+
* tagged.
|
|
142
|
+
*
|
|
143
|
+
* @param botUserId the bot's own user id (from `auth.test`), surfaced when the
|
|
144
|
+
* bot is @-mentioned so the ACL admits the group message.
|
|
145
|
+
*/
|
|
146
|
+
export function extractSlackMentions(event, botUserId) {
|
|
147
|
+
const source = event.subtype === "message_changed" && event.message ? event.message : event;
|
|
148
|
+
const text = typeof source.text === "string" ? source.text : "";
|
|
149
|
+
const out = [];
|
|
150
|
+
const seen = new Set();
|
|
151
|
+
const push = (id) => {
|
|
152
|
+
if (!id || seen.has(id))
|
|
153
|
+
return;
|
|
154
|
+
seen.add(id);
|
|
155
|
+
out.push(id);
|
|
156
|
+
};
|
|
157
|
+
let botMentioned = event.type === "app_mention";
|
|
158
|
+
// Scan every <@U…> / <@U…|label> user-mention token in the text.
|
|
159
|
+
const re = /<@([A-Z0-9]+)(?:\|[^>]*)?>/g;
|
|
160
|
+
let m;
|
|
161
|
+
while ((m = re.exec(text)) !== null) {
|
|
162
|
+
const id = m[1];
|
|
163
|
+
if (!id)
|
|
164
|
+
continue;
|
|
165
|
+
if (botUserId && id === botUserId) {
|
|
166
|
+
botMentioned = true;
|
|
167
|
+
continue; // the bot's own id is pushed last (after the addressed check)
|
|
168
|
+
}
|
|
169
|
+
push(id);
|
|
170
|
+
}
|
|
171
|
+
if (botMentioned && botUserId)
|
|
172
|
+
push(botUserId);
|
|
173
|
+
return out;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* A short display name for the sender. A legacy bot-posted `username` wins; else
|
|
177
|
+
* a resolved display name (when a `resolveName` reader from the user directory is
|
|
178
|
+
* supplied AND it has already cached the sender's name); else the raw user id
|
|
179
|
+
* (`U…`) — display-name resolution needs a `users.info` call that the directory
|
|
180
|
+
* primes in the background, so the first message from a never-seen user surfaces
|
|
181
|
+
* the id and later messages surface the name.
|
|
182
|
+
*/
|
|
183
|
+
export function buildSlackSenderName(event, resolveName) {
|
|
184
|
+
const source = event.subtype === "message_changed" && event.message ? event.message : event;
|
|
185
|
+
const username = typeof source["username"] === "string" ? source["username"] : "";
|
|
186
|
+
if (username)
|
|
187
|
+
return username;
|
|
188
|
+
const user = typeof source.user === "string" ? source.user : "";
|
|
189
|
+
if (user) {
|
|
190
|
+
const resolved = resolveName?.(user);
|
|
191
|
+
if (resolved)
|
|
192
|
+
return resolved;
|
|
193
|
+
}
|
|
194
|
+
return user || undefined;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Reply-context (what message this inbound quotes), when it's a threaded reply.
|
|
198
|
+
* Slack threads are flat — a reply carries `thread_ts` (the parent's ts) but the
|
|
199
|
+
* event does NOT inline the parent's text — so the context surfaces the parent
|
|
200
|
+
* message id (`thread_ts`) and leaves `body` undefined (the pipeline can fetch
|
|
201
|
+
* the parent if it needs the excerpt). Returns undefined for a top-level
|
|
202
|
+
* message (no `thread_ts`, or `thread_ts === ts` which is the thread ROOT, not a
|
|
203
|
+
* reply).
|
|
204
|
+
*/
|
|
205
|
+
export function extractSlackReplyContext(event) {
|
|
206
|
+
const source = event.subtype === "message_changed" && event.message ? event.message : event;
|
|
207
|
+
const threadTs = source.thread_ts;
|
|
208
|
+
const ts = source.ts;
|
|
209
|
+
if (typeof threadTs !== "string" || !threadTs)
|
|
210
|
+
return undefined;
|
|
211
|
+
// The thread ROOT carries thread_ts === ts; only a genuine reply quotes a
|
|
212
|
+
// DIFFERENT parent.
|
|
213
|
+
if (typeof ts === "string" && ts === threadTs)
|
|
214
|
+
return undefined;
|
|
215
|
+
return { messageId: threadTs };
|
|
216
|
+
}
|
|
217
|
+
/* ───────────────────────── media detection ───────────────────────── */
|
|
218
|
+
/** A downloadable file is one with a private url that isn't a tombstone (deleted). */
|
|
219
|
+
function isDownloadableFile(f) {
|
|
220
|
+
if (!f || f.mode === "tombstone")
|
|
221
|
+
return false;
|
|
222
|
+
return Boolean(f.url_private || f.url_private_download);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Cheap presence probe — does this message carry a downloadable file? Walks
|
|
226
|
+
* `event.files[]` (an edit reads the nested message's files) but never touches
|
|
227
|
+
* the network, so the connection layer can DEFER the actual download until AFTER
|
|
228
|
+
* the central access gate admits the sender (mirrors Telegram's deferred-media
|
|
229
|
+
* discipline). Mirrors `hasInboundMedia`.
|
|
230
|
+
*/
|
|
231
|
+
export function hasInboundMedia(event) {
|
|
232
|
+
const source = event.subtype === "message_changed" && event.message ? event.message : event;
|
|
233
|
+
const files = Array.isArray(source.files) ? source.files : [];
|
|
234
|
+
return files.some(isDownloadableFile);
|
|
235
|
+
}
|
|
236
|
+
/** The list of downloadable files on a message (an edit reads the nested message). */
|
|
237
|
+
export function resolveInboundFiles(event) {
|
|
238
|
+
const source = event.subtype === "message_changed" && event.message ? event.message : event;
|
|
239
|
+
const files = Array.isArray(source.files) ? source.files : [];
|
|
240
|
+
return files.filter(isDownloadableFile);
|
|
241
|
+
}
|
|
242
|
+
/** The Brigade media-kind of a Slack file, from its mimetype / filetype. */
|
|
243
|
+
export function resolveSlackFileKind(f) {
|
|
244
|
+
const mime = (f.mimetype ?? "").toLowerCase();
|
|
245
|
+
const type = (f.filetype ?? "").toLowerCase();
|
|
246
|
+
if (mime.startsWith("image/") || /^(png|jpg|jpeg|gif|webp|bmp|heic)$/.test(type))
|
|
247
|
+
return "image";
|
|
248
|
+
if (mime.startsWith("video/") || /^(mp4|mov|webm|mkv|avi)$/.test(type))
|
|
249
|
+
return "video";
|
|
250
|
+
// Slack voice memos arrive as audio with a dedicated subtype; treat m4a/ogg
|
|
251
|
+
// voice-ish containers as "voice", other audio as "audio".
|
|
252
|
+
if (mime.startsWith("audio/") || /^(mp3|m4a|ogg|wav|flac|aac)$/.test(type)) {
|
|
253
|
+
return type === "m4a" || type === "ogg" ? "voice" : "audio";
|
|
254
|
+
}
|
|
255
|
+
return "document";
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=inbound-extras.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbound-extras.js","sourceRoot":"","sources":["../../../../src/agents/channels/slack/inbound-extras.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Slack channel — public surface. */
|
|
2
|
+
export { createSlackAdapter, buildReactionNote, SLACK_CAPABILITIES, type CreateSlackAdapterOptions, type SlackAdapter, } from "./adapter.js";
|
|
3
|
+
export { listSlackAccountIds, resolveSlackAccount, resolveSlackBotToken, resolveSlackAppToken, resolveSlackSigningSecret, resolveSlackUserToken, slackChannelEnabled, slackEventsConfig, slackLiveStreamEnabled, slackStreamThrottleMs, slackSurfaceReasoning, slackThreadIdleTtlMs, SLACK_BOT_TOKEN_ENV_VAR, SLACK_APP_TOKEN_ENV_VAR, SLACK_SIGNING_SECRET_ENV_VAR, SLACK_CHANNEL_ID, SLACK_DEFAULT_ACCOUNT_ID, type ResolvedSlackAccount, type SlackEventsConfig, } from "./account-config.js";
|
|
4
|
+
export { buildSlackApprovalMessage, buildSlackApprovalText, parseSlackApprovalAction, type SlackApprovalMessage, } from "./approval-native.js";
|
|
5
|
+
export { resolveSlackApprover } from "./approval-authorize.js";
|
|
6
|
+
export { buildSlackApprovalBlocks, buildSlackInlineKeyboard, extractBlockActionPayload, SLACK_APPROVAL_ACTION_ID, SLACK_GENERAL_ACTION_ID, type SlackActionsBlock, type SlackBlock, } from "./blocks.js";
|
|
7
|
+
export { buildSlackCommandManifest, normalizeSlackCommandName, type SlackSlashCommand, } from "./command-menu.js";
|
|
8
|
+
export { connectSlack, isSlackUnauthorized, redactSlackToken, slackBackoffDelay, type ConnectSlackArgs, type SlackConnection, type SlackInboundMessage, } from "./connection.js";
|
|
9
|
+
export { listSlackDirectoryPeers, listSlackDirectoryGroups, type SlackDirectoryEntry, type SlackDirectoryQuery, type SlackDirectoryWebClientLike, } from "./directory-live.js";
|
|
10
|
+
export { markdownToSlackMrkdwn, slackMrkdwnIsEmpty, escapeSlackMrkdwn } from "./format.js";
|
|
11
|
+
export { createSlackPlugin, type SlackPluginDeps, type SlackPluginHandle } from "./plugin.js";
|
|
12
|
+
export { probeSlack, type SlackProbeResult, type SlackProbeBot, type SlackProbeTeam } from "./probe.js";
|
|
13
|
+
export { buildSlackWebhookRoute, verifySlackSignature, parseSlackBody, SLACK_SIGNATURE_HEADER, SLACK_TIMESTAMP_HEADER, } from "./webhook.js";
|
|
14
|
+
export { slackModule } from "./module.js";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/slack/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,yBAAyB,EAC9B,KAAK,YAAY,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,uBAAuB,EACvB,4BAA4B,EAC5B,gBAAgB,EAChB,wBAAwB,EACxB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,KAAK,oBAAoB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EACN,wBAAwB,EACxB,wBAAwB,EACxB,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,UAAU,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,yBAAyB,EACzB,yBAAyB,EACzB,KAAK,iBAAiB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,mBAAmB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,uBAAuB,EACvB,wBAAwB,EACxB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,GAChC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9F,OAAO,EAAE,UAAU,EAAE,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AACxG,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,sBAAsB,EACtB,sBAAsB,GACtB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Slack channel — public surface. */
|
|
2
|
+
export { createSlackAdapter, buildReactionNote, SLACK_CAPABILITIES, } from "./adapter.js";
|
|
3
|
+
export { listSlackAccountIds, resolveSlackAccount, resolveSlackBotToken, resolveSlackAppToken, resolveSlackSigningSecret, resolveSlackUserToken, slackChannelEnabled, slackEventsConfig, slackLiveStreamEnabled, slackStreamThrottleMs, slackSurfaceReasoning, slackThreadIdleTtlMs, SLACK_BOT_TOKEN_ENV_VAR, SLACK_APP_TOKEN_ENV_VAR, SLACK_SIGNING_SECRET_ENV_VAR, SLACK_CHANNEL_ID, SLACK_DEFAULT_ACCOUNT_ID, } from "./account-config.js";
|
|
4
|
+
export { buildSlackApprovalMessage, buildSlackApprovalText, parseSlackApprovalAction, } from "./approval-native.js";
|
|
5
|
+
export { resolveSlackApprover } from "./approval-authorize.js";
|
|
6
|
+
export { buildSlackApprovalBlocks, buildSlackInlineKeyboard, extractBlockActionPayload, SLACK_APPROVAL_ACTION_ID, SLACK_GENERAL_ACTION_ID, } from "./blocks.js";
|
|
7
|
+
export { buildSlackCommandManifest, normalizeSlackCommandName, } from "./command-menu.js";
|
|
8
|
+
export { connectSlack, isSlackUnauthorized, redactSlackToken, slackBackoffDelay, } from "./connection.js";
|
|
9
|
+
export { listSlackDirectoryPeers, listSlackDirectoryGroups, } from "./directory-live.js";
|
|
10
|
+
export { markdownToSlackMrkdwn, slackMrkdwnIsEmpty, escapeSlackMrkdwn } from "./format.js";
|
|
11
|
+
export { createSlackPlugin } from "./plugin.js";
|
|
12
|
+
export { probeSlack } from "./probe.js";
|
|
13
|
+
export { buildSlackWebhookRoute, verifySlackSignature, parseSlackBody, SLACK_SIGNATURE_HEADER, SLACK_TIMESTAMP_HEADER, } from "./webhook.js";
|
|
14
|
+
export { slackModule } from "./module.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/agents/channels/slack/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,GAGlB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,uBAAuB,EACvB,4BAA4B,EAC5B,gBAAgB,EAChB,wBAAwB,GAGxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,GAExB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EACN,wBAAwB,EACxB,wBAAwB,EACxB,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,GAGvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,yBAAyB,EACzB,yBAAyB,GAEzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,GAIjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,uBAAuB,EACvB,wBAAwB,GAIxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,iBAAiB,EAAgD,MAAM,aAAa,CAAC;AAC9F,OAAO,EAAE,UAAU,EAAkE,MAAM,YAAY,CAAC;AACxG,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,sBAAsB,EACtB,sBAAsB,GACtB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack media helpers — inbound download + outbound upload construction.
|
|
3
|
+
*
|
|
4
|
+
* INBOUND: Slack doesn't push file bytes; a message event carries file objects
|
|
5
|
+
* with an authenticated `url_private`. To get the bytes we GET that URL with an
|
|
6
|
+
* `Authorization: Bearer <botToken>` header (a plain fetch returns the login
|
|
7
|
+
* HTML otherwise). Bytes are saved under
|
|
8
|
+
* `~/.brigade/channels/slack/media/<YYYY-MM-DD>/<fileId>.<ext>` so the agent can
|
|
9
|
+
* `read` the attachment by path. In convex mode the cache relocates to the OS
|
|
10
|
+
* cache dir (never under ~/.brigade, to respect the strict-zero guard).
|
|
11
|
+
*
|
|
12
|
+
* OUTBOUND: `uploadSlackFile` posts a local path's bytes via the Web API's
|
|
13
|
+
* `files.uploadV2`, after running the path through Brigade's outbound media-path
|
|
14
|
+
* guard so a prompt-injected "send ~/.ssh/id_rsa" can't exfiltrate a secret. The
|
|
15
|
+
* `@slack/web-api` `WebClient` is injected (not imported here) so this module
|
|
16
|
+
* stays dependency-light + unit-testable.
|
|
17
|
+
*/
|
|
18
|
+
import { type InboundMediaAttachment, type OutboundMedia } from "../sdk.js";
|
|
19
|
+
import { type SlackFileObject } from "./inbound-extras.js";
|
|
20
|
+
/**
|
|
21
|
+
* Defensive ceiling on an inbound file download. Slack's own per-file limit is
|
|
22
|
+
* generous; we cap at 50 MB so a huge upload can't blow out memory. Anything
|
|
23
|
+
* larger is skipped (the message still reaches the agent without the
|
|
24
|
+
* attachment).
|
|
25
|
+
*/
|
|
26
|
+
declare const MAX_BYTES: number;
|
|
27
|
+
/**
|
|
28
|
+
* True when `rawUrl` is an https URL whose host is a Slack file-CDN host (or a
|
|
29
|
+
* subdomain of one). Anything else (non-https, a non-Slack host, or an
|
|
30
|
+
* unparseable URL) returns false so the caller refuses to fetch with the token.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isAllowedSlackFileUrl(rawUrl: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Bounded retry for the transient file fetch. Slack's file CDN occasionally
|
|
35
|
+
* blips on a 5xx or a network reset; one or two quick retries turn a dropped
|
|
36
|
+
* attachment into a delivered one. The caller still wraps the whole thing in a
|
|
37
|
+
* try/catch that degrades to `null`, so an exhausted retry never breaks message
|
|
38
|
+
* delivery. Mirrors Telegram's `withMediaRetry`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function withSlackRetry<T>(fn: () => Promise<T>, attempts?: number): Promise<T>;
|
|
41
|
+
export interface DownloadSlackFileArgs {
|
|
42
|
+
/** The Slack file object (from the message event's `files[]`). */
|
|
43
|
+
file: SlackFileObject;
|
|
44
|
+
/** Bot (or user) token — sent as `Authorization: Bearer …`. NEVER logged. */
|
|
45
|
+
token: string;
|
|
46
|
+
/** Injectable fetch (defaults to global fetch) — lets tests stub the download. */
|
|
47
|
+
fetchImpl?: typeof fetch;
|
|
48
|
+
/** Logger so a failed download logs without crashing the inbound flow. */
|
|
49
|
+
log?: (msg: string, meta?: Record<string, unknown>) => void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Download one inbound Slack file to disk and return its normalized descriptor,
|
|
53
|
+
* or `null` when the file couldn't be fetched (no url / too big / network error
|
|
54
|
+
* / tombstoned). Never throws — a download glitch must not break message
|
|
55
|
+
* delivery. The `Authorization: Bearer` header is REQUIRED; a plain GET of
|
|
56
|
+
* `url_private` returns Slack's HTML login page, not the bytes.
|
|
57
|
+
*/
|
|
58
|
+
export declare function downloadSlackFile(args: DownloadSlackFileArgs): Promise<InboundMediaAttachment | null>;
|
|
59
|
+
/** The minimal `files.uploadV2` surface the outbound path drives — injectable for tests. */
|
|
60
|
+
export interface SlackUploadApi {
|
|
61
|
+
files: {
|
|
62
|
+
uploadV2(args: {
|
|
63
|
+
channel_id: string;
|
|
64
|
+
file: unknown;
|
|
65
|
+
filename: string;
|
|
66
|
+
initial_comment?: string;
|
|
67
|
+
thread_ts?: string;
|
|
68
|
+
}): Promise<unknown>;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export interface UploadSlackFileArgs {
|
|
72
|
+
/** The Web API client (`WebClient`) the upload runs through. */
|
|
73
|
+
client: SlackUploadApi;
|
|
74
|
+
/** Destination channel id. */
|
|
75
|
+
channelId: string;
|
|
76
|
+
/** The local media to upload. */
|
|
77
|
+
media: OutboundMedia;
|
|
78
|
+
/** Optional thread to upload into. */
|
|
79
|
+
threadId?: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Upload a local file (image / video / audio / doc) to a Slack channel via
|
|
83
|
+
* `files.uploadV2`, after running the path through Brigade's outbound
|
|
84
|
+
* media-path guard. Throws a clear operator-facing error when the guard refuses
|
|
85
|
+
* the path (the `send_media` tool surfaces it). The file is streamed from disk;
|
|
86
|
+
* the caption rides as `initial_comment`.
|
|
87
|
+
*/
|
|
88
|
+
export declare function uploadSlackFile(args: UploadSlackFileArgs): Promise<void>;
|
|
89
|
+
export { MAX_BYTES as SLACK_MEDIA_MAX_BYTES };
|
|
90
|
+
//# sourceMappingURL=media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/slack/media.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAUH,OAAO,EAEN,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAIjF;;;;;GAKG;AACH,QAAA,MAAM,SAAS,QAAmB,CAAC;AAYnC;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAU7D;AAqCD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,SAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAWtF;AAED,MAAM,WAAW,qBAAqB;IACrC,kEAAkE;IAClE,IAAI,EAAE,eAAe,CAAC;IACtB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CAC5D;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAgE3G;AAED,4FAA4F;AAC5F,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE;QACN,QAAQ,CAAC,IAAI,EAAE;YACd,UAAU,EAAE,MAAM,CAAC;YACnB,IAAI,EAAE,OAAO,CAAC;YACd,QAAQ,EAAE,MAAM,CAAC;YACjB,eAAe,CAAC,EAAE,MAAM,CAAC;YACzB,SAAS,CAAC,EAAE,MAAM,CAAC;SACnB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACrB,CAAC;CACF;AAED,MAAM,WAAW,mBAAmB;IACnC,gEAAgE;IAChE,MAAM,EAAE,cAAc,CAAC;IACvB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,KAAK,EAAE,aAAa,CAAC;IACrB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAc9E;AAED,OAAO,EAAE,SAAS,IAAI,qBAAqB,EAAE,CAAC"}
|