@spinabot/brigade 1.5.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/dist/agents/agent-loop.d.ts.map +1 -1
  2. package/dist/agents/agent-loop.js +51 -1
  3. package/dist/agents/agent-loop.js.map +1 -1
  4. package/dist/agents/channels/bundled-channel-metas.d.ts +4 -0
  5. package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -1
  6. package/dist/agents/channels/bundled-channel-metas.js +22 -0
  7. package/dist/agents/channels/bundled-channel-metas.js.map +1 -1
  8. package/dist/agents/channels/discord/account-config.d.ts +117 -0
  9. package/dist/agents/channels/discord/account-config.d.ts.map +1 -0
  10. package/dist/agents/channels/discord/account-config.js +260 -0
  11. package/dist/agents/channels/discord/account-config.js.map +1 -0
  12. package/dist/agents/channels/discord/adapter.d.ts +56 -0
  13. package/dist/agents/channels/discord/adapter.d.ts.map +1 -0
  14. package/dist/agents/channels/discord/adapter.js +526 -0
  15. package/dist/agents/channels/discord/adapter.js.map +1 -0
  16. package/dist/agents/channels/discord/approval-authorize.d.ts +43 -0
  17. package/dist/agents/channels/discord/approval-authorize.d.ts.map +1 -0
  18. package/dist/agents/channels/discord/approval-authorize.js +71 -0
  19. package/dist/agents/channels/discord/approval-authorize.js.map +1 -0
  20. package/dist/agents/channels/discord/approval-native.d.ts +68 -0
  21. package/dist/agents/channels/discord/approval-native.d.ts.map +1 -0
  22. package/dist/agents/channels/discord/approval-native.js +81 -0
  23. package/dist/agents/channels/discord/approval-native.js.map +1 -0
  24. package/dist/agents/channels/discord/command-menu.d.ts +49 -0
  25. package/dist/agents/channels/discord/command-menu.d.ts.map +1 -0
  26. package/dist/agents/channels/discord/command-menu.js +73 -0
  27. package/dist/agents/channels/discord/command-menu.js.map +1 -0
  28. package/dist/agents/channels/discord/components.d.ts +97 -0
  29. package/dist/agents/channels/discord/components.d.ts.map +1 -0
  30. package/dist/agents/channels/discord/components.js +131 -0
  31. package/dist/agents/channels/discord/components.js.map +1 -0
  32. package/dist/agents/channels/discord/connection.d.ts +387 -0
  33. package/dist/agents/channels/discord/connection.d.ts.map +1 -0
  34. package/dist/agents/channels/discord/connection.js +786 -0
  35. package/dist/agents/channels/discord/connection.js.map +1 -0
  36. package/dist/agents/channels/discord/draft-stream.d.ts +92 -0
  37. package/dist/agents/channels/discord/draft-stream.d.ts.map +1 -0
  38. package/dist/agents/channels/discord/draft-stream.js +213 -0
  39. package/dist/agents/channels/discord/draft-stream.js.map +1 -0
  40. package/dist/agents/channels/discord/format.d.ts +55 -0
  41. package/dist/agents/channels/discord/format.d.ts.map +1 -0
  42. package/dist/agents/channels/discord/format.js +247 -0
  43. package/dist/agents/channels/discord/format.js.map +1 -0
  44. package/dist/agents/channels/discord/inbound-extras.d.ts +220 -0
  45. package/dist/agents/channels/discord/inbound-extras.d.ts.map +1 -0
  46. package/dist/agents/channels/discord/inbound-extras.js +351 -0
  47. package/dist/agents/channels/discord/inbound-extras.js.map +1 -0
  48. package/dist/agents/channels/discord/index.d.ts +14 -0
  49. package/dist/agents/channels/discord/index.d.ts.map +1 -0
  50. package/dist/agents/channels/discord/index.js +14 -0
  51. package/dist/agents/channels/discord/index.js.map +1 -0
  52. package/dist/agents/channels/discord/media.d.ts +85 -0
  53. package/dist/agents/channels/discord/media.d.ts.map +1 -0
  54. package/dist/agents/channels/discord/media.js +242 -0
  55. package/dist/agents/channels/discord/media.js.map +1 -0
  56. package/dist/agents/channels/discord/module.d.ts +15 -0
  57. package/dist/agents/channels/discord/module.d.ts.map +1 -0
  58. package/dist/agents/channels/discord/module.js +22 -0
  59. package/dist/agents/channels/discord/module.js.map +1 -0
  60. package/dist/agents/channels/discord/plugin.d.ts +73 -0
  61. package/dist/agents/channels/discord/plugin.d.ts.map +1 -0
  62. package/dist/agents/channels/discord/plugin.js +303 -0
  63. package/dist/agents/channels/discord/plugin.js.map +1 -0
  64. package/dist/agents/channels/discord/probe.d.ts +93 -0
  65. package/dist/agents/channels/discord/probe.d.ts.map +1 -0
  66. package/dist/agents/channels/discord/probe.js +158 -0
  67. package/dist/agents/channels/discord/probe.js.map +1 -0
  68. package/dist/agents/channels/discord/reasoning-lane.d.ts +42 -0
  69. package/dist/agents/channels/discord/reasoning-lane.d.ts.map +1 -0
  70. package/dist/agents/channels/discord/reasoning-lane.js +68 -0
  71. package/dist/agents/channels/discord/reasoning-lane.js.map +1 -0
  72. package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
  73. package/dist/agents/channels/inbound-pipeline.js +65 -7
  74. package/dist/agents/channels/inbound-pipeline.js.map +1 -1
  75. package/dist/agents/channels/manager.d.ts.map +1 -1
  76. package/dist/agents/channels/manager.js +18 -0
  77. package/dist/agents/channels/manager.js.map +1 -1
  78. package/dist/agents/channels/sdk.d.ts +4 -0
  79. package/dist/agents/channels/sdk.d.ts.map +1 -1
  80. package/dist/agents/channels/sdk.js +4 -0
  81. package/dist/agents/channels/sdk.js.map +1 -1
  82. package/dist/agents/channels/slack/account-config.d.ts +172 -0
  83. package/dist/agents/channels/slack/account-config.d.ts.map +1 -0
  84. package/dist/agents/channels/slack/account-config.js +353 -0
  85. package/dist/agents/channels/slack/account-config.js.map +1 -0
  86. package/dist/agents/channels/slack/account-registry.d.ts +45 -0
  87. package/dist/agents/channels/slack/account-registry.d.ts.map +1 -0
  88. package/dist/agents/channels/slack/account-registry.js +58 -0
  89. package/dist/agents/channels/slack/account-registry.js.map +1 -0
  90. package/dist/agents/channels/slack/adapter.d.ts +66 -0
  91. package/dist/agents/channels/slack/adapter.d.ts.map +1 -0
  92. package/dist/agents/channels/slack/adapter.js +547 -0
  93. package/dist/agents/channels/slack/adapter.js.map +1 -0
  94. package/dist/agents/channels/slack/approval-authorize.d.ts +43 -0
  95. package/dist/agents/channels/slack/approval-authorize.d.ts.map +1 -0
  96. package/dist/agents/channels/slack/approval-authorize.js +71 -0
  97. package/dist/agents/channels/slack/approval-authorize.js.map +1 -0
  98. package/dist/agents/channels/slack/approval-native.d.ts +70 -0
  99. package/dist/agents/channels/slack/approval-native.d.ts.map +1 -0
  100. package/dist/agents/channels/slack/approval-native.js +85 -0
  101. package/dist/agents/channels/slack/approval-native.js.map +1 -0
  102. package/dist/agents/channels/slack/blocks.d.ts +125 -0
  103. package/dist/agents/channels/slack/blocks.d.ts.map +1 -0
  104. package/dist/agents/channels/slack/blocks.js +145 -0
  105. package/dist/agents/channels/slack/blocks.js.map +1 -0
  106. package/dist/agents/channels/slack/command-menu.d.ts +44 -0
  107. package/dist/agents/channels/slack/command-menu.d.ts.map +1 -0
  108. package/dist/agents/channels/slack/command-menu.js +66 -0
  109. package/dist/agents/channels/slack/command-menu.js.map +1 -0
  110. package/dist/agents/channels/slack/connection.d.ts +422 -0
  111. package/dist/agents/channels/slack/connection.d.ts.map +1 -0
  112. package/dist/agents/channels/slack/connection.js +1042 -0
  113. package/dist/agents/channels/slack/connection.js.map +1 -0
  114. package/dist/agents/channels/slack/directory-live.d.ts +129 -0
  115. package/dist/agents/channels/slack/directory-live.d.ts.map +1 -0
  116. package/dist/agents/channels/slack/directory-live.js +148 -0
  117. package/dist/agents/channels/slack/directory-live.js.map +1 -0
  118. package/dist/agents/channels/slack/draft-stream.d.ts +93 -0
  119. package/dist/agents/channels/slack/draft-stream.d.ts.map +1 -0
  120. package/dist/agents/channels/slack/draft-stream.js +218 -0
  121. package/dist/agents/channels/slack/draft-stream.js.map +1 -0
  122. package/dist/agents/channels/slack/format.d.ts +41 -0
  123. package/dist/agents/channels/slack/format.d.ts.map +1 -0
  124. package/dist/agents/channels/slack/format.js +271 -0
  125. package/dist/agents/channels/slack/format.js.map +1 -0
  126. package/dist/agents/channels/slack/inbound-extras.d.ts +179 -0
  127. package/dist/agents/channels/slack/inbound-extras.d.ts.map +1 -0
  128. package/dist/agents/channels/slack/inbound-extras.js +257 -0
  129. package/dist/agents/channels/slack/inbound-extras.js.map +1 -0
  130. package/dist/agents/channels/slack/index.d.ts +15 -0
  131. package/dist/agents/channels/slack/index.d.ts.map +1 -0
  132. package/dist/agents/channels/slack/index.js +15 -0
  133. package/dist/agents/channels/slack/index.js.map +1 -0
  134. package/dist/agents/channels/slack/media.d.ts +90 -0
  135. package/dist/agents/channels/slack/media.d.ts.map +1 -0
  136. package/dist/agents/channels/slack/media.js +215 -0
  137. package/dist/agents/channels/slack/media.js.map +1 -0
  138. package/dist/agents/channels/slack/module.d.ts +26 -0
  139. package/dist/agents/channels/slack/module.d.ts.map +1 -0
  140. package/dist/agents/channels/slack/module.js +67 -0
  141. package/dist/agents/channels/slack/module.js.map +1 -0
  142. package/dist/agents/channels/slack/plugin.d.ts +69 -0
  143. package/dist/agents/channels/slack/plugin.d.ts.map +1 -0
  144. package/dist/agents/channels/slack/plugin.js +318 -0
  145. package/dist/agents/channels/slack/plugin.js.map +1 -0
  146. package/dist/agents/channels/slack/probe.d.ts +72 -0
  147. package/dist/agents/channels/slack/probe.d.ts.map +1 -0
  148. package/dist/agents/channels/slack/probe.js +103 -0
  149. package/dist/agents/channels/slack/probe.js.map +1 -0
  150. package/dist/agents/channels/slack/proxy-agent.d.ts +30 -0
  151. package/dist/agents/channels/slack/proxy-agent.d.ts.map +1 -0
  152. package/dist/agents/channels/slack/proxy-agent.js +44 -0
  153. package/dist/agents/channels/slack/proxy-agent.js.map +1 -0
  154. package/dist/agents/channels/slack/reasoning-lane.d.ts +42 -0
  155. package/dist/agents/channels/slack/reasoning-lane.d.ts.map +1 -0
  156. package/dist/agents/channels/slack/reasoning-lane.js +68 -0
  157. package/dist/agents/channels/slack/reasoning-lane.js.map +1 -0
  158. package/dist/agents/channels/slack/user-directory.d.ts +69 -0
  159. package/dist/agents/channels/slack/user-directory.d.ts.map +1 -0
  160. package/dist/agents/channels/slack/user-directory.js +94 -0
  161. package/dist/agents/channels/slack/user-directory.js.map +1 -0
  162. package/dist/agents/channels/slack/webhook.d.ts +89 -0
  163. package/dist/agents/channels/slack/webhook.d.ts.map +1 -0
  164. package/dist/agents/channels/slack/webhook.js +228 -0
  165. package/dist/agents/channels/slack/webhook.js.map +1 -0
  166. package/dist/agents/channels/telegram/format.d.ts.map +1 -1
  167. package/dist/agents/channels/telegram/format.js +17 -1
  168. package/dist/agents/channels/telegram/format.js.map +1 -1
  169. package/dist/agents/channels/telegram/webhook.d.ts.map +1 -1
  170. package/dist/agents/channels/telegram/webhook.js +7 -1
  171. package/dist/agents/channels/telegram/webhook.js.map +1 -1
  172. package/dist/agents/extensions/modules/index.d.ts.map +1 -1
  173. package/dist/agents/extensions/modules/index.js +10 -0
  174. package/dist/agents/extensions/modules/index.js.map +1 -1
  175. package/dist/agents/tools/cron-tool.d.ts.map +1 -1
  176. package/dist/agents/tools/cron-tool.js +4 -1
  177. package/dist/agents/tools/cron-tool.js.map +1 -1
  178. package/dist/buildstamp.json +1 -1
  179. package/dist/core/auth-bridge.d.ts +1 -0
  180. package/dist/core/auth-bridge.d.ts.map +1 -1
  181. package/dist/core/auth-bridge.js +46 -1
  182. package/dist/core/auth-bridge.js.map +1 -1
  183. package/dist/core/server.d.ts.map +1 -1
  184. package/dist/core/server.js +40 -5
  185. package/dist/core/server.js.map +1 -1
  186. package/dist/cron/isolated-agent/run-executor.d.ts +11 -0
  187. package/dist/cron/isolated-agent/run-executor.d.ts.map +1 -1
  188. package/dist/cron/isolated-agent/run-executor.js +20 -4
  189. package/dist/cron/isolated-agent/run-executor.js.map +1 -1
  190. package/dist/cron/types.d.ts +8 -0
  191. package/dist/cron/types.d.ts.map +1 -1
  192. package/dist/system-prompt/assembler.d.ts.map +1 -1
  193. package/dist/system-prompt/assembler.js +4 -2
  194. package/dist/system-prompt/assembler.js.map +1 -1
  195. package/package.json +5 -1
@@ -0,0 +1,351 @@
1
+ /**
2
+ * Pure extractors that turn a discord.js message / reaction / interaction into
3
+ * the normalized fields Brigade's `InboundMessage` carries. No network, no side
4
+ * effects — every function here is deterministic over its argument so they're
5
+ * trivial to unit-test without a live gateway.
6
+ *
7
+ * Discord's wire shape (rich discord.js objects, not raw JSON like Slack's
8
+ * Events API) differs from Slack/Telegram in load-bearing ways, so the logic is
9
+ * a Brigade-native re-implementation that models the SHAPE of
10
+ * `slack/inbound-extras.ts` (raw payload → normalized signals) while speaking
11
+ * Discord semantics:
12
+ *
13
+ * - Text arrives as `message.content` peppered with Discord TOKENS: `<@123>`
14
+ * / `<@!123>` (user/member), `<@&456>` (role), `<#789>` (channel),
15
+ * `<:name:111>` / `<a:name:111>` (custom emoji). {@link extractDiscordText}
16
+ * expands user/channel/role mentions into readable plain text the agent can
17
+ * parse (`@alex`, `#general`), resolving names via an injected cache when one
18
+ * is supplied (the cached guild/client primes them in the background).
19
+ * - The bot is "addressed" when its own user id appears as a `<@123>` mention.
20
+ * {@link extractDiscordMentions} surfaces the bot's id when addressed so the
21
+ * central group ACL admits the message — exactly as Slack surfaces the bot's
22
+ * id. Every OTHER user mention is surfaced too.
23
+ * - Channel kind: a DM channel → `direct`; a guild text channel / thread →
24
+ * `group` (see {@link discordChannelType}).
25
+ * - Threads ride on the channel being a thread (`threadId` = the thread
26
+ * channel id); the parent text channel is the conversation root.
27
+ * - Reply context rides on `message.reference.messageId`.
28
+ * - Attachments arrive as `message.attachments` (each a CDN `url` + metadata);
29
+ * the connection layer DEFERS the byte download until the access gate admits
30
+ * the sender (mirrors Slack/Telegram's deferred-media discipline).
31
+ *
32
+ * Brigade's CENTRAL inbound pipeline owns the actual ACL / mention / routing
33
+ * decision — these helpers only surface the raw signals it reads.
34
+ */
35
+ /* ───────────────────────── small helpers ───────────────────────── */
36
+ /**
37
+ * Reject control-byte payloads (a binary blob masquerading as text). Tab / LF /
38
+ * CR are allowed; any other C0 control char marks the run as non-text and it's
39
+ * dropped so the agent never ingests raw binary. Mirrors Slack's
40
+ * `isBinaryContent`.
41
+ */
42
+ function isBinaryContent(text) {
43
+ for (let i = 0; i < text.length; i++) {
44
+ const code = text.charCodeAt(i);
45
+ if (code <= 0x1f && code !== 0x09 && code !== 0x0a && code !== 0x0d)
46
+ return true;
47
+ }
48
+ return false;
49
+ }
50
+ /** Normalize a discord.js Collection / Map / iterable / array into a plain array. */
51
+ function toArray(coll) {
52
+ if (!coll)
53
+ return [];
54
+ if (Array.isArray(coll))
55
+ return coll;
56
+ if (coll instanceof Map)
57
+ return [...coll.values()];
58
+ // A discord.js Collection extends Map, so the branch above catches it; this is
59
+ // the generic-iterable fallback for any other shape.
60
+ if (typeof coll[Symbol.iterator] === "function")
61
+ return [...coll];
62
+ return [];
63
+ }
64
+ /* ───────────────────────── token expansion ───────────────────────── */
65
+ /**
66
+ * Expand the Discord message TOKENS in a run of text into readable plain text:
67
+ * - `<@123>` / `<@!123>` → `@alex` (resolved name) or `@123`
68
+ * - `<@&456>` → `@role` (resolved) or `@&456`
69
+ * - `<#789>` → `#general` (resolved) or `#789`
70
+ * - `<:name:111>` / `<a:name:111>` → `:name:` (the readable emoji shortcode)
71
+ *
72
+ * Names resolve via the injected lookups (the connection primes them from the
73
+ * cached guild/client); without a resolver the bare id is surfaced. Pure +
74
+ * deterministic.
75
+ */
76
+ export function expandDiscordTokens(text, resolve) {
77
+ if (!text)
78
+ return "";
79
+ return text.replace(/<(a?):([A-Za-z0-9_]{2,32}):\d+>|<@&(\d+)>|<@!?(\d+)>|<#(\d+)>/g, (whole, _anim, emojiName, roleId, userId, channelId) => {
80
+ if (typeof emojiName === "string" && emojiName)
81
+ return `:${emojiName}:`;
82
+ if (typeof roleId === "string" && roleId) {
83
+ const name = resolve?.role?.(roleId);
84
+ return name ? `@${name}` : `@&${roleId}`;
85
+ }
86
+ if (typeof userId === "string" && userId) {
87
+ const name = resolve?.user?.(userId);
88
+ return name ? `@${name}` : `@${userId}`;
89
+ }
90
+ if (typeof channelId === "string" && channelId) {
91
+ const name = resolve?.channel?.(channelId);
92
+ return name ? `#${name}` : `#${channelId}`;
93
+ }
94
+ return whole;
95
+ });
96
+ }
97
+ /**
98
+ * The agent-facing plain text of a Discord message. Token-expanded (mentions /
99
+ * channels / roles / emoji → readable text). Binary blobs are dropped to "".
100
+ */
101
+ export function extractDiscordText(message, resolve) {
102
+ const raw = typeof message.content === "string" ? message.content : "";
103
+ if (!raw || isBinaryContent(raw))
104
+ return "";
105
+ return expandDiscordTokens(raw, resolve).trim();
106
+ }
107
+ /* ───────────────────────── chat type + thread + reply ───────────────────────── */
108
+ /** Discord channel-type enum values Brigade distinguishes. */
109
+ const CHANNEL_TYPE_DM = 1;
110
+ const CHANNEL_TYPE_GROUP_DM = 3;
111
+ const THREAD_TYPE_VALUES = new Set([10, 11, 12]); // announcement / public / private thread
112
+ /** True when a discord.js channel is a thread (uses `isThread()` when present, else the type enum). */
113
+ export function isThreadChannel(channel) {
114
+ if (!channel)
115
+ return false;
116
+ if (typeof channel.isThread === "function")
117
+ return channel.isThread();
118
+ return typeof channel.type === "number" && THREAD_TYPE_VALUES.has(channel.type);
119
+ }
120
+ /** True when a discord.js channel is a DM (uses `isDMBased()` when present, else the type enum). */
121
+ export function isDmChannel(channel) {
122
+ if (!channel)
123
+ return false;
124
+ if (typeof channel.isDMBased === "function")
125
+ return channel.isDMBased();
126
+ return channel.type === CHANNEL_TYPE_DM || channel.type === CHANNEL_TYPE_GROUP_DM;
127
+ }
128
+ /**
129
+ * Discord channel → Brigade chat type. A 1:1 DM → `direct`; a guild text channel
130
+ * or a thread → `group`. A group DM (rare for bots) → `group`. When no channel
131
+ * object is available, a missing `guildId` implies a DM.
132
+ */
133
+ export function discordChannelType(message) {
134
+ const channel = message.channel;
135
+ if (channel) {
136
+ if (isDmChannel(channel) && channel.type !== CHANNEL_TYPE_GROUP_DM)
137
+ return "direct";
138
+ return "group";
139
+ }
140
+ // No channel object: a message with no guild is a DM.
141
+ return message.guildId ? "group" : "direct";
142
+ }
143
+ /**
144
+ * The thread channel id when the message landed in a thread, else undefined. The
145
+ * thread's own channel id is the thread id (Discord threads ARE channels).
146
+ */
147
+ export function discordThreadId(message) {
148
+ const channel = message.channel;
149
+ if (channel && isThreadChannel(channel)) {
150
+ return typeof channel.id === "string" ? channel.id : typeof message.channelId === "string" ? message.channelId : undefined;
151
+ }
152
+ return undefined;
153
+ }
154
+ /**
155
+ * Reply-context (what message this inbound quotes), when it's a reply. Discord
156
+ * carries `message.reference.messageId` (the replied-to message id) but does NOT
157
+ * inline that message's text — so the context surfaces the parent message id and
158
+ * leaves `body` undefined (the connection can fetch the parent if it needs the
159
+ * excerpt). Returns undefined for a non-reply.
160
+ */
161
+ export function extractDiscordReplyContext(message) {
162
+ const ref = message.reference;
163
+ const id = ref?.messageId;
164
+ if (typeof id !== "string" || !id)
165
+ return undefined;
166
+ return { messageId: id };
167
+ }
168
+ /* ───────────────────────── mentions ───────────────────────── */
169
+ /**
170
+ * Channel-native ids of users addressed in this message. Brigade's central group
171
+ * ACL treats a group message as "addressed to the bot" when the bot's own id
172
+ * appears in `mentions`; without this a group message never reaches the agent.
173
+ * So when the bot's own `<@id>` mention appears in the text we surface the bot's
174
+ * id (passed in). Every OTHER user mention is surfaced too so the pipeline sees
175
+ * who else was tagged.
176
+ *
177
+ * We scan BOTH the resolved `message.mentions.users` collection (when present)
178
+ * AND the raw `<@id>` / `<@!id>` tokens in the content, unioned + deduped — the
179
+ * collection is authoritative on a live message, the token scan is the fallback
180
+ * for a fake / a partial.
181
+ *
182
+ * @param botUserId the bot's own user id, surfaced when the bot is mentioned so
183
+ * the ACL admits the group message.
184
+ */
185
+ export function extractDiscordMentions(message, botUserId) {
186
+ const out = [];
187
+ const seen = new Set();
188
+ let botMentioned = false;
189
+ const push = (id) => {
190
+ if (!id || seen.has(id))
191
+ return;
192
+ if (botUserId && id === botUserId) {
193
+ botMentioned = true;
194
+ return; // the bot's own id is pushed LAST (after the addressed check)
195
+ }
196
+ seen.add(id);
197
+ out.push(id);
198
+ };
199
+ for (const u of toArray(message.mentions?.users)) {
200
+ if (typeof u?.id === "string")
201
+ push(u.id);
202
+ }
203
+ const content = typeof message.content === "string" ? message.content : "";
204
+ const re = /<@!?(\d+)>/g;
205
+ let m;
206
+ while ((m = re.exec(content)) !== null) {
207
+ if (m[1])
208
+ push(m[1]);
209
+ }
210
+ if (botMentioned && botUserId) {
211
+ out.push(botUserId);
212
+ }
213
+ return out;
214
+ }
215
+ /**
216
+ * The guild-role ids the sending member holds, surfaced so the 8-tier route
217
+ * resolver's `binding.guild+roles` tier can match. Role NAMES are never used —
218
+ * only ids. Returns `[]` for a DM (no member) or when no roles resolve.
219
+ *
220
+ * Handles the three shapes the `member.roles` value can take:
221
+ * - discord.js `GuildMemberRoleManager` — a `.cache` Collection (Map subclass)
222
+ * keyed by role id; we read the keys (the role ids).
223
+ * - a plain array / iterable of role-id strings (a fake / partial).
224
+ * - absent / null (a DM, or a message with no resolved member) → `[]`.
225
+ *
226
+ * Pure + deterministic; deduped, with the `@everyone` role (whose id equals the
227
+ * guild id and is implicit for everyone) left in as-is — the resolver only
228
+ * matches ids a binding explicitly lists, so a stray everyone id is harmless.
229
+ */
230
+ export function extractDiscordMemberRoleIds(message) {
231
+ const roles = message.member?.roles;
232
+ if (!roles)
233
+ return [];
234
+ const out = [];
235
+ const seen = new Set();
236
+ const push = (id) => {
237
+ if (typeof id !== "string")
238
+ return;
239
+ const trimmed = id.trim();
240
+ if (!trimmed || seen.has(trimmed))
241
+ return;
242
+ seen.add(trimmed);
243
+ out.push(trimmed);
244
+ };
245
+ // Plain string array / iterable of ids (a fake or a partial).
246
+ if (Array.isArray(roles)) {
247
+ for (const id of roles)
248
+ push(id);
249
+ return out;
250
+ }
251
+ // A `GuildMemberRoleManager` exposes `.cache` (a Collection ⊂ Map keyed by id).
252
+ const cache = roles.cache;
253
+ if (cache) {
254
+ if (cache instanceof Map) {
255
+ for (const key of cache.keys())
256
+ push(key);
257
+ return out;
258
+ }
259
+ if (typeof cache[Symbol.iterator] === "function") {
260
+ // A Collection iterates `[key, value]` pairs; a bare value-iterable yields
261
+ // role-ish objects. Read the id off whichever shape arrives.
262
+ for (const item of cache) {
263
+ if (Array.isArray(item))
264
+ push(item[0]);
265
+ else if (item && typeof item === "object" && "id" in item)
266
+ push(item.id);
267
+ else
268
+ push(item);
269
+ }
270
+ return out;
271
+ }
272
+ }
273
+ // A bare iterable of ids on the manager itself (some fakes).
274
+ if (typeof roles[Symbol.iterator] === "function") {
275
+ for (const id of roles)
276
+ push(id);
277
+ }
278
+ return out;
279
+ }
280
+ /**
281
+ * A short display name for the sender. A guild nickname wins (most specific),
282
+ * then the member/global display name, then the username, then the raw id.
283
+ */
284
+ export function buildDiscordSenderName(message) {
285
+ const nickname = message.member?.nickname;
286
+ if (typeof nickname === "string" && nickname.trim())
287
+ return nickname.trim();
288
+ const memberDisplay = message.member?.displayName;
289
+ if (typeof memberDisplay === "string" && memberDisplay.trim())
290
+ return memberDisplay.trim();
291
+ const author = message.author;
292
+ const display = author?.displayName ?? author?.globalName;
293
+ if (typeof display === "string" && display.trim())
294
+ return display.trim();
295
+ const username = author?.username;
296
+ if (typeof username === "string" && username.trim())
297
+ return username.trim();
298
+ return typeof author?.id === "string" ? author.id : undefined;
299
+ }
300
+ /* ───────────────────────── media detection ───────────────────────── */
301
+ /** The Discord voice-message flag bit (1 << 13). discord.js exposes `flags.has(...)`; we also accept the raw number. */
302
+ const VOICE_MESSAGE_FLAG = 1 << 13;
303
+ /** A downloadable attachment is one carrying a url. */
304
+ function isDownloadableAttachment(a) {
305
+ return Boolean(a && (a.url || a.proxyURL));
306
+ }
307
+ /**
308
+ * Cheap presence probe — does this message carry a downloadable attachment?
309
+ * Walks `message.attachments` but never touches the network, so the connection
310
+ * layer can DEFER the actual download until AFTER the central access gate admits
311
+ * the sender (mirrors Slack/Telegram). Mirrors `hasInboundMedia`.
312
+ */
313
+ export function hasInboundMedia(message) {
314
+ return toArray(message.attachments).some(isDownloadableAttachment);
315
+ }
316
+ /** The list of downloadable attachments on a message. */
317
+ export function resolveInboundAttachments(message) {
318
+ return toArray(message.attachments).filter(isDownloadableAttachment);
319
+ }
320
+ /** True when an attachment carries the Discord voice-message flag. */
321
+ function isVoiceAttachment(a) {
322
+ const flags = a.flags;
323
+ if (typeof flags === "number")
324
+ return (flags & VOICE_MESSAGE_FLAG) !== 0;
325
+ if (flags && typeof flags.has === "function") {
326
+ try {
327
+ return Boolean(flags.has(VOICE_MESSAGE_FLAG));
328
+ }
329
+ catch {
330
+ return false;
331
+ }
332
+ }
333
+ return false;
334
+ }
335
+ /** The Brigade media-kind of a Discord attachment, from its contentType / filename / voice flag. */
336
+ export function resolveDiscordAttachmentKind(a) {
337
+ if (isVoiceAttachment(a))
338
+ return "voice";
339
+ const mime = (a.contentType ?? "").toLowerCase();
340
+ const name = (a.name ?? "").toLowerCase();
341
+ const ext = name.includes(".") ? name.slice(name.lastIndexOf(".") + 1) : "";
342
+ if (mime.startsWith("image/") || /^(png|jpg|jpeg|gif|webp|bmp|heic)$/.test(ext))
343
+ return "image";
344
+ if (mime.startsWith("video/") || /^(mp4|mov|webm|mkv|avi)$/.test(ext))
345
+ return "video";
346
+ if (mime.startsWith("audio/") || /^(mp3|m4a|ogg|wav|flac|aac)$/.test(ext)) {
347
+ return ext === "ogg" || ext === "m4a" ? "voice" : "audio";
348
+ }
349
+ return "document";
350
+ }
351
+ //# sourceMappingURL=inbound-extras.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound-extras.js","sourceRoot":"","sources":["../../../../src/agents/channels/discord/inbound-extras.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /** Discord channel — public surface. */
2
+ export { createDiscordAdapter, buildReactionNote, DISCORD_CAPABILITIES, type CreateDiscordAdapterOptions, type DiscordAdapter, } from "./adapter.js";
3
+ export { listDiscordAccountIds, resolveDiscordAccount, resolveDiscordBotToken, resolveDiscordProxyUrl, discordChannelEnabled, discordLiveStreamEnabled, discordStreamThrottleMs, discordSurfaceReasoning, discordThreadIdleTtlMs, maskProxyUrl, stripBotPrefix, DISCORD_BOT_TOKEN_ENV_VAR, DISCORD_CHANNEL_ID, DISCORD_DEFAULT_ACCOUNT_ID, type ResolvedDiscordAccount, } from "./account-config.js";
4
+ export { buildDiscordApprovalMessage, buildDiscordApprovalText, parseDiscordApprovalAction, type DiscordApprovalMessage, } from "./approval-native.js";
5
+ export { resolveDiscordApprover } from "./approval-authorize.js";
6
+ export { buildDiscordApprovalRows, buildDiscordButtonRows, sanitizeDiscordCustomId, DISCORD_BUTTON_STYLE, DISCORD_BUTTONS_PER_ROW, DISCORD_CUSTOM_ID_MAX_CHARS, DISCORD_MAX_ROWS, type DiscordActionRow, type DiscordButtonSpec, } from "./components.js";
7
+ export { buildDiscordCommandManifest, normalizeDiscordCommandName, type DiscordApplicationCommand, } from "./command-menu.js";
8
+ export { connectDiscord, discordBackoffDelay, isDiscordUnauthorized, redactDiscordToken, type ConnectDiscordArgs, type DiscordConnection, type DiscordInboundMessage, } from "./connection.js";
9
+ export { downloadDiscordAttachment, buildDiscordAttachment, isAllowedDiscordAttachmentUrl, type DiscordOutboundAttachment, } from "./media.js";
10
+ export { markdownToDiscord, discordTextIsEmpty, DISCORD_MESSAGE_LIMIT } from "./format.js";
11
+ export { createDiscordPlugin, type DiscordPluginDeps, type DiscordPluginHandle } from "./plugin.js";
12
+ export { probeDiscord, decodeMessageContentIntent, MESSAGE_CONTENT_DISABLED_WARNING, type DiscordProbeResult, type DiscordProbeBot, type MessageContentIntentState, } from "./probe.js";
13
+ export { discordModule } from "./module.js";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/discord/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AAExC,OAAO,EACN,oBAAoB,EACpB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,cAAc,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,YAAY,EACZ,cAAc,EACd,yBAAyB,EACzB,kBAAkB,EAClB,0BAA0B,EAC1B,KAAK,sBAAsB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,2BAA2B,EAC3B,wBAAwB,EACxB,0BAA0B,EAC1B,KAAK,sBAAsB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EACN,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,EACvB,2BAA2B,EAC3B,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,2BAA2B,EAC3B,2BAA2B,EAC3B,KAAK,yBAAyB,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,yBAAyB,EACzB,sBAAsB,EACtB,6BAA6B,EAC7B,KAAK,yBAAyB,GAC9B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,EACN,YAAY,EACZ,0BAA0B,EAC1B,gCAAgC,EAChC,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,yBAAyB,GAC9B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,14 @@
1
+ /** Discord channel — public surface. */
2
+ export { createDiscordAdapter, buildReactionNote, DISCORD_CAPABILITIES, } from "./adapter.js";
3
+ export { listDiscordAccountIds, resolveDiscordAccount, resolveDiscordBotToken, resolveDiscordProxyUrl, discordChannelEnabled, discordLiveStreamEnabled, discordStreamThrottleMs, discordSurfaceReasoning, discordThreadIdleTtlMs, maskProxyUrl, stripBotPrefix, DISCORD_BOT_TOKEN_ENV_VAR, DISCORD_CHANNEL_ID, DISCORD_DEFAULT_ACCOUNT_ID, } from "./account-config.js";
4
+ export { buildDiscordApprovalMessage, buildDiscordApprovalText, parseDiscordApprovalAction, } from "./approval-native.js";
5
+ export { resolveDiscordApprover } from "./approval-authorize.js";
6
+ export { buildDiscordApprovalRows, buildDiscordButtonRows, sanitizeDiscordCustomId, DISCORD_BUTTON_STYLE, DISCORD_BUTTONS_PER_ROW, DISCORD_CUSTOM_ID_MAX_CHARS, DISCORD_MAX_ROWS, } from "./components.js";
7
+ export { buildDiscordCommandManifest, normalizeDiscordCommandName, } from "./command-menu.js";
8
+ export { connectDiscord, discordBackoffDelay, isDiscordUnauthorized, redactDiscordToken, } from "./connection.js";
9
+ export { downloadDiscordAttachment, buildDiscordAttachment, isAllowedDiscordAttachmentUrl, } from "./media.js";
10
+ export { markdownToDiscord, discordTextIsEmpty, DISCORD_MESSAGE_LIMIT } from "./format.js";
11
+ export { createDiscordPlugin } from "./plugin.js";
12
+ export { probeDiscord, decodeMessageContentIntent, MESSAGE_CONTENT_DISABLED_WARNING, } from "./probe.js";
13
+ export { discordModule } from "./module.js";
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/agents/channels/discord/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AAExC,OAAO,EACN,oBAAoB,EACpB,iBAAiB,EACjB,oBAAoB,GAGpB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,YAAY,EACZ,cAAc,EACd,yBAAyB,EACzB,kBAAkB,EAClB,0BAA0B,GAE1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,2BAA2B,EAC3B,wBAAwB,EACxB,0BAA0B,GAE1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EACN,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,EACvB,2BAA2B,EAC3B,gBAAgB,GAGhB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,2BAA2B,EAC3B,2BAA2B,GAE3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,GAIlB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,yBAAyB,EACzB,sBAAsB,EACtB,6BAA6B,GAE7B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAoD,MAAM,aAAa,CAAC;AACpG,OAAO,EACN,YAAY,EACZ,0BAA0B,EAC1B,gCAAgC,GAIhC,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Discord media helpers — inbound download + outbound upload construction.
3
+ *
4
+ * INBOUND: a Discord message attachment carries a public CDN `url`
5
+ * (cdn.discordapp.com / media.discordapp.net). We GET that URL (NO auth header —
6
+ * Discord attachment URLs are public, unlike Slack's `url_private`) and save the
7
+ * bytes under `~/.brigade/channels/discord/media/<YYYY-MM-DD>/<id>.<ext>` so the
8
+ * agent can `read` the attachment by path. In convex mode the cache relocates to
9
+ * the OS cache dir (never under ~/.brigade, to respect the strict-zero guard).
10
+ *
11
+ * OUTBOUND: `buildDiscordAttachment` validates a local path through Brigade's
12
+ * outbound media-path guard (so a prompt-injected "send ~/.ssh/id_rsa" can't
13
+ * exfiltrate a secret), then returns the `{ path, name }` the connection wraps
14
+ * in a discord.js `AttachmentBuilder`. The builder itself lives in the
15
+ * connection (which imports discord.js); this module stays dependency-light +
16
+ * unit-testable.
17
+ *
18
+ * SSRF GUARD: even though Discord URLs are public, we REQUIRE https + a Discord
19
+ * CDN host before fetching. Without this a prompt-injected / spoofed message
20
+ * could carry `http://169.254.169.254/…` (cloud metadata) or any attacker host
21
+ * and Brigade would fetch it — a classic SSRF. Mirrors `slack/media.ts`.
22
+ */
23
+ import { type InboundMediaAttachment, type OutboundMedia } from "../sdk.js";
24
+ import { type DiscordAttachmentLike } from "./inbound-extras.js";
25
+ /**
26
+ * Defensive ceiling on an inbound attachment download. Discord's own per-file
27
+ * limit is generous (and tiered by server boost); we cap at 50 MB so a huge
28
+ * upload can't blow out memory. Anything larger is skipped (the message still
29
+ * reaches the agent without the attachment).
30
+ */
31
+ declare const MAX_BYTES: number;
32
+ /**
33
+ * True when `rawUrl` is an https URL whose host is a Discord CDN host (or a
34
+ * subdomain of one). Anything else (non-https, a non-Discord host, or an
35
+ * unparseable URL) returns false so the caller refuses to fetch.
36
+ */
37
+ export declare function isAllowedDiscordAttachmentUrl(rawUrl: string): boolean;
38
+ /**
39
+ * Bounded retry for the transient attachment fetch. Discord's CDN occasionally
40
+ * blips on a 5xx or a network reset; one or two quick retries turn a dropped
41
+ * attachment into a delivered one. The caller still wraps the whole thing in a
42
+ * try/catch that degrades to `null`, so an exhausted retry never breaks message
43
+ * delivery. Mirrors `slack/media.ts`'s `withSlackRetry`.
44
+ */
45
+ export declare function withDiscordRetry<T>(fn: () => Promise<T>, attempts?: number): Promise<T>;
46
+ export interface DownloadDiscordAttachmentArgs {
47
+ /** The Discord attachment object (from the message's `attachments`). */
48
+ attachment: DiscordAttachmentLike;
49
+ /** Injectable fetch (defaults to global fetch) — lets tests stub the download. */
50
+ fetchImpl?: typeof fetch;
51
+ /** Logger so a failed download logs without crashing the inbound flow. */
52
+ log?: (msg: string, meta?: Record<string, unknown>) => void;
53
+ }
54
+ /**
55
+ * Download one inbound Discord attachment to disk and return its normalized
56
+ * descriptor, or `null` when it couldn't be fetched (no url / too big / network
57
+ * error / non-Discord host). Never throws — a download glitch must not break
58
+ * message delivery.
59
+ */
60
+ export declare function downloadDiscordAttachment(args: DownloadDiscordAttachmentArgs): Promise<InboundMediaAttachment | null>;
61
+ /** The validated outbound attachment shape the connection wraps in an `AttachmentBuilder`. */
62
+ export interface DiscordOutboundAttachment {
63
+ /** Local filesystem path (guard-validated). */
64
+ path: string;
65
+ /** Display filename. */
66
+ name: string;
67
+ /** Optional caption — sent as the message content alongside the file. */
68
+ caption?: string;
69
+ }
70
+ /**
71
+ * Validate + shape a local file for outbound upload. Runs the path through
72
+ * Brigade's outbound media-path guard and throws a clear operator-facing error
73
+ * when the guard refuses it (the `send_media` tool surfaces it). Returns the
74
+ * `{ path, name, caption? }` the connection turns into a discord.js
75
+ * `AttachmentBuilder`.
76
+ *
77
+ * Filename precedence: an explicit `media.fileName` wins; otherwise the path's
78
+ * basename. When the chosen name has NO extension we append one inferred from
79
+ * `media.kind` (image→png, video→mp4, audio→mp3, voice→ogg) so Discord detects
80
+ * the file type instead of treating it as an opaque blob (an extensionless
81
+ * `image` upload otherwise renders as a generic file, not an inline preview).
82
+ */
83
+ export declare function buildDiscordAttachment(media: OutboundMedia): DiscordOutboundAttachment;
84
+ export { MAX_BYTES as DISCORD_MEDIA_MAX_BYTES };
85
+ //# sourceMappingURL=media.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/discord/media.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAUH,OAAO,EAA6B,KAAK,sBAAsB,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AACvG,OAAO,EAAgC,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAI/F;;;;;GAKG;AACH,QAAA,MAAM,SAAS,QAAmB,CAAC;AAUnC;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAUrE;AAuCD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,SAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAWxF;AAED,MAAM,WAAW,6BAA6B;IAC7C,wEAAwE;IACxE,UAAU,EAAE,qBAAqB,CAAC;IAClC,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;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,6BAA6B,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CA4D3H;AAED,8FAA8F;AAC9F,MAAM,WAAW,yBAAyB;IACzC,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAoBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,GAAG,yBAAyB,CAiBtF;AAED,OAAO,EAAE,SAAS,IAAI,uBAAuB,EAAE,CAAC"}