@spinabot/brigade 1.6.0 → 1.7.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.
Files changed (194) 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 +2 -0
  5. package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -1
  6. package/dist/agents/channels/bundled-channel-metas.js +11 -0
  7. package/dist/agents/channels/bundled-channel-metas.js.map +1 -1
  8. package/dist/agents/channels/discord/account-config.d.ts +177 -0
  9. package/dist/agents/channels/discord/account-config.d.ts.map +1 -0
  10. package/dist/agents/channels/discord/account-config.js +349 -0
  11. package/dist/agents/channels/discord/account-config.js.map +1 -0
  12. package/dist/agents/channels/discord/adapter.d.ts +79 -0
  13. package/dist/agents/channels/discord/adapter.d.ts.map +1 -0
  14. package/dist/agents/channels/discord/adapter.js +693 -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/component-blocks.d.ts +108 -0
  29. package/dist/agents/channels/discord/component-blocks.d.ts.map +1 -0
  30. package/dist/agents/channels/discord/component-blocks.js +113 -0
  31. package/dist/agents/channels/discord/component-blocks.js.map +1 -0
  32. package/dist/agents/channels/discord/components.d.ts +175 -0
  33. package/dist/agents/channels/discord/components.d.ts.map +1 -0
  34. package/dist/agents/channels/discord/components.js +220 -0
  35. package/dist/agents/channels/discord/components.js.map +1 -0
  36. package/dist/agents/channels/discord/connection.d.ts +570 -0
  37. package/dist/agents/channels/discord/connection.d.ts.map +1 -0
  38. package/dist/agents/channels/discord/connection.js +1600 -0
  39. package/dist/agents/channels/discord/connection.js.map +1 -0
  40. package/dist/agents/channels/discord/directory-cache.d.ts +47 -0
  41. package/dist/agents/channels/discord/directory-cache.d.ts.map +1 -0
  42. package/dist/agents/channels/discord/directory-cache.js +131 -0
  43. package/dist/agents/channels/discord/directory-cache.js.map +1 -0
  44. package/dist/agents/channels/discord/directory-live.d.ts +61 -0
  45. package/dist/agents/channels/discord/directory-live.d.ts.map +1 -0
  46. package/dist/agents/channels/discord/directory-live.js +140 -0
  47. package/dist/agents/channels/discord/directory-live.js.map +1 -0
  48. package/dist/agents/channels/discord/draft-stream.d.ts +92 -0
  49. package/dist/agents/channels/discord/draft-stream.d.ts.map +1 -0
  50. package/dist/agents/channels/discord/draft-stream.js +213 -0
  51. package/dist/agents/channels/discord/draft-stream.js.map +1 -0
  52. package/dist/agents/channels/discord/format.d.ts +70 -0
  53. package/dist/agents/channels/discord/format.d.ts.map +1 -0
  54. package/dist/agents/channels/discord/format.js +303 -0
  55. package/dist/agents/channels/discord/format.js.map +1 -0
  56. package/dist/agents/channels/discord/guilds.d.ts +25 -0
  57. package/dist/agents/channels/discord/guilds.d.ts.map +1 -0
  58. package/dist/agents/channels/discord/guilds.js +46 -0
  59. package/dist/agents/channels/discord/guilds.js.map +1 -0
  60. package/dist/agents/channels/discord/inbound-extras.d.ts +377 -0
  61. package/dist/agents/channels/discord/inbound-extras.d.ts.map +1 -0
  62. package/dist/agents/channels/discord/inbound-extras.js +589 -0
  63. package/dist/agents/channels/discord/inbound-extras.js.map +1 -0
  64. package/dist/agents/channels/discord/index.d.ts +21 -0
  65. package/dist/agents/channels/discord/index.d.ts.map +1 -0
  66. package/dist/agents/channels/discord/index.js +21 -0
  67. package/dist/agents/channels/discord/index.js.map +1 -0
  68. package/dist/agents/channels/discord/media.d.ts +85 -0
  69. package/dist/agents/channels/discord/media.d.ts.map +1 -0
  70. package/dist/agents/channels/discord/media.js +242 -0
  71. package/dist/agents/channels/discord/media.js.map +1 -0
  72. package/dist/agents/channels/discord/modal-registry.d.ts +89 -0
  73. package/dist/agents/channels/discord/modal-registry.d.ts.map +1 -0
  74. package/dist/agents/channels/discord/modal-registry.js +104 -0
  75. package/dist/agents/channels/discord/modal-registry.js.map +1 -0
  76. package/dist/agents/channels/discord/modals.d.ts +100 -0
  77. package/dist/agents/channels/discord/modals.d.ts.map +1 -0
  78. package/dist/agents/channels/discord/modals.js +124 -0
  79. package/dist/agents/channels/discord/modals.js.map +1 -0
  80. package/dist/agents/channels/discord/module.d.ts +15 -0
  81. package/dist/agents/channels/discord/module.d.ts.map +1 -0
  82. package/dist/agents/channels/discord/module.js +22 -0
  83. package/dist/agents/channels/discord/module.js.map +1 -0
  84. package/dist/agents/channels/discord/permission-audit.d.ts +43 -0
  85. package/dist/agents/channels/discord/permission-audit.d.ts.map +1 -0
  86. package/dist/agents/channels/discord/permission-audit.js +192 -0
  87. package/dist/agents/channels/discord/permission-audit.js.map +1 -0
  88. package/dist/agents/channels/discord/plugin.d.ts +89 -0
  89. package/dist/agents/channels/discord/plugin.d.ts.map +1 -0
  90. package/dist/agents/channels/discord/plugin.js +372 -0
  91. package/dist/agents/channels/discord/plugin.js.map +1 -0
  92. package/dist/agents/channels/discord/probe.d.ts +115 -0
  93. package/dist/agents/channels/discord/probe.d.ts.map +1 -0
  94. package/dist/agents/channels/discord/probe.js +193 -0
  95. package/dist/agents/channels/discord/probe.js.map +1 -0
  96. package/dist/agents/channels/discord/reasoning-lane.d.ts +42 -0
  97. package/dist/agents/channels/discord/reasoning-lane.d.ts.map +1 -0
  98. package/dist/agents/channels/discord/reasoning-lane.js +68 -0
  99. package/dist/agents/channels/discord/reasoning-lane.js.map +1 -0
  100. package/dist/agents/channels/discord/rest-actions.d.ts +346 -0
  101. package/dist/agents/channels/discord/rest-actions.d.ts.map +1 -0
  102. package/dist/agents/channels/discord/rest-actions.js +559 -0
  103. package/dist/agents/channels/discord/rest-actions.js.map +1 -0
  104. package/dist/agents/channels/discord/rest-components.d.ts +122 -0
  105. package/dist/agents/channels/discord/rest-components.d.ts.map +1 -0
  106. package/dist/agents/channels/discord/rest-components.js +243 -0
  107. package/dist/agents/channels/discord/rest-components.js.map +1 -0
  108. package/dist/agents/channels/discord/security-audit.d.ts +29 -0
  109. package/dist/agents/channels/discord/security-audit.d.ts.map +1 -0
  110. package/dist/agents/channels/discord/security-audit.js +94 -0
  111. package/dist/agents/channels/discord/security-audit.js.map +1 -0
  112. package/dist/agents/channels/discord/security-doctor.d.ts +43 -0
  113. package/dist/agents/channels/discord/security-doctor.d.ts.map +1 -0
  114. package/dist/agents/channels/discord/security-doctor.js +83 -0
  115. package/dist/agents/channels/discord/security-doctor.js.map +1 -0
  116. package/dist/agents/channels/discord/status-issues.d.ts +37 -0
  117. package/dist/agents/channels/discord/status-issues.d.ts.map +1 -0
  118. package/dist/agents/channels/discord/status-issues.js +66 -0
  119. package/dist/agents/channels/discord/status-issues.js.map +1 -0
  120. package/dist/agents/channels/discord/subagent-thread-binding-store.d.ts +57 -0
  121. package/dist/agents/channels/discord/subagent-thread-binding-store.d.ts.map +1 -0
  122. package/dist/agents/channels/discord/subagent-thread-binding-store.js +98 -0
  123. package/dist/agents/channels/discord/subagent-thread-binding-store.js.map +1 -0
  124. package/dist/agents/channels/discord/subagent-thread-binding.d.ts +95 -0
  125. package/dist/agents/channels/discord/subagent-thread-binding.d.ts.map +1 -0
  126. package/dist/agents/channels/discord/subagent-thread-binding.js +208 -0
  127. package/dist/agents/channels/discord/subagent-thread-binding.js.map +1 -0
  128. package/dist/agents/channels/discord/system-events.d.ts +31 -0
  129. package/dist/agents/channels/discord/system-events.d.ts.map +1 -0
  130. package/dist/agents/channels/discord/system-events.js +74 -0
  131. package/dist/agents/channels/discord/system-events.js.map +1 -0
  132. package/dist/agents/channels/general-callback.d.ts +12 -0
  133. package/dist/agents/channels/general-callback.d.ts.map +1 -1
  134. package/dist/agents/channels/general-callback.js +18 -0
  135. package/dist/agents/channels/general-callback.js.map +1 -1
  136. package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
  137. package/dist/agents/channels/inbound-pipeline.js +70 -10
  138. package/dist/agents/channels/inbound-pipeline.js.map +1 -1
  139. package/dist/agents/channels/sdk.d.ts +2 -0
  140. package/dist/agents/channels/sdk.d.ts.map +1 -1
  141. package/dist/agents/channels/sdk.js +2 -0
  142. package/dist/agents/channels/sdk.js.map +1 -1
  143. package/dist/agents/extensions/modules/index.d.ts.map +1 -1
  144. package/dist/agents/extensions/modules/index.js +5 -0
  145. package/dist/agents/extensions/modules/index.js.map +1 -1
  146. package/dist/agents/extensions/types.d.ts +7 -0
  147. package/dist/agents/extensions/types.d.ts.map +1 -1
  148. package/dist/agents/extensions/types.js.map +1 -1
  149. package/dist/agents/subagent-announce-delivery.d.ts +10 -0
  150. package/dist/agents/subagent-announce-delivery.d.ts.map +1 -1
  151. package/dist/agents/subagent-announce-delivery.js +1 -0
  152. package/dist/agents/subagent-announce-delivery.js.map +1 -1
  153. package/dist/agents/subagent-completion-bridge.d.ts.map +1 -1
  154. package/dist/agents/subagent-completion-bridge.js +81 -0
  155. package/dist/agents/subagent-completion-bridge.js.map +1 -1
  156. package/dist/agents/subagent-spawn.d.ts.map +1 -1
  157. package/dist/agents/subagent-spawn.js +57 -4
  158. package/dist/agents/subagent-spawn.js.map +1 -1
  159. package/dist/agents/tools/cron-tool.d.ts.map +1 -1
  160. package/dist/agents/tools/cron-tool.js +4 -1
  161. package/dist/agents/tools/cron-tool.js.map +1 -1
  162. package/dist/agents/tools/discord-action-tool.d.ts +224 -0
  163. package/dist/agents/tools/discord-action-tool.d.ts.map +1 -0
  164. package/dist/agents/tools/discord-action-tool.js +848 -0
  165. package/dist/agents/tools/discord-action-tool.js.map +1 -0
  166. package/dist/agents/tools/registry.d.ts.map +1 -1
  167. package/dist/agents/tools/registry.js +21 -0
  168. package/dist/agents/tools/registry.js.map +1 -1
  169. package/dist/agents/tools/sessions/index.d.ts +8 -0
  170. package/dist/agents/tools/sessions/index.d.ts.map +1 -1
  171. package/dist/agents/tools/sessions/index.js +15 -3
  172. package/dist/agents/tools/sessions/index.js.map +1 -1
  173. package/dist/buildstamp.json +1 -1
  174. package/dist/cli/commands/channels.d.ts +2 -0
  175. package/dist/cli/commands/channels.d.ts.map +1 -1
  176. package/dist/cli/commands/channels.js +58 -1
  177. package/dist/cli/commands/channels.js.map +1 -1
  178. package/dist/core/auth-bridge.d.ts +1 -0
  179. package/dist/core/auth-bridge.d.ts.map +1 -1
  180. package/dist/core/auth-bridge.js +46 -1
  181. package/dist/core/auth-bridge.js.map +1 -1
  182. package/dist/core/server.d.ts.map +1 -1
  183. package/dist/core/server.js +18 -2
  184. package/dist/core/server.js.map +1 -1
  185. package/dist/cron/isolated-agent/run-executor.d.ts +11 -0
  186. package/dist/cron/isolated-agent/run-executor.d.ts.map +1 -1
  187. package/dist/cron/isolated-agent/run-executor.js +20 -4
  188. package/dist/cron/isolated-agent/run-executor.js.map +1 -1
  189. package/dist/cron/types.d.ts +8 -0
  190. package/dist/cron/types.d.ts.map +1 -1
  191. package/dist/system-prompt/assembler.d.ts.map +1 -1
  192. package/dist/system-prompt/assembler.js +4 -2
  193. package/dist/system-prompt/assembler.js.map +1 -1
  194. package/package.json +2 -1
@@ -0,0 +1,372 @@
1
+ /**
2
+ * Discord `ChannelPlugin` — the multi-ACCOUNT contract surface.
3
+ *
4
+ * Mirrors `slack/plugin.ts`: wraps `createDiscordAdapter()` (the per-connection
5
+ * implementation) with the lifecycle adapters the `ChannelPluginManager`
6
+ * consumes, so an operator can run MORE THAN ONE Discord bot at once via:
7
+ *
8
+ * channels.discord = {
9
+ * enabled: true,
10
+ * accounts: [
11
+ * { id: "main", botToken: "…AAA" },
12
+ * { id: "labs", botToken: "…BBB" },
13
+ * ],
14
+ * }
15
+ *
16
+ * - `config.listAccountIds` / `resolveAccount` → multi-account discovery
17
+ * - `gateway.startAccount` / `stopAccount` → per-account bot lifecycle
18
+ * - `outbound.sendText` / `sendMedia` → routes by `target.accountId`
19
+ * - per-account approval-dispatcher registration → an exec-gate prompt raised
20
+ * by a turn on (discord, labs) replies on (discord, labs), not the default
21
+ *
22
+ * Per-account state lives in a `Map<accountId, AccountRuntime>` held in this
23
+ * closure — one Gateway connection per account, partitioned token resolution per
24
+ * `channels.discord.accounts[].botToken`. Inbound dispatch reuses the shared
25
+ * `runChannelInboundPipeline` so the multi-account path carries the identical
26
+ * ACL + debounce + abort + approval-reply + approval-callback surface as the
27
+ * legacy single-adapter manager.
28
+ *
29
+ * The legacy single-account `createDiscordAdapter` (started by the legacy
30
+ * `startChannels` manager) STEPS ASIDE when >1 account is configured — its
31
+ * `isConfigured` returns false for the default account in that case (mirrors
32
+ * Slack), so the two paths never double-start a bot.
33
+ *
34
+ * Discord has no events-mode HTTP route (the Gateway is the only inbound
35
+ * transport), so — unlike Slack's plugin — there is NO per-account webhook-sink
36
+ * registry to populate.
37
+ */
38
+ // Channel SDK barrel — the SINGLE import surface for the multi-account
39
+ // `ChannelPlugin` contract + every sub-adapter type + the shared inbound
40
+ // pipeline + the approval router + the gateway boot args. A multi-account
41
+ // channel authors entirely from here.
42
+ import { buildBundledCommands, createInboundPipelineContext, createSubsystemLogger, registerChannelApprovalDispatcher, removeChannelApprovalDispatcher, runChannelInboundPipeline, DISCORD_CHANNEL_META, } from "../sdk.js";
43
+ import { listDiscordAccountIds, resolveDiscordAccount, resolveDiscordBotToken, DISCORD_CHANNEL_ID, DISCORD_DEFAULT_ACCOUNT_ID, } from "./account-config.js";
44
+ import { createDiscordAdapter, DISCORD_CAPABILITIES } from "./adapter.js";
45
+ import { probeDiscord } from "./probe.js";
46
+ import { auditDiscordChannelPermissions } from "./permission-audit.js";
47
+ import { collectDiscordSecurityAuditFindings } from "./security-audit.js";
48
+ import { collectDiscordStatusIssues } from "./status-issues.js";
49
+ const log = createSubsystemLogger("channels/discord/plugin");
50
+ // Single source of truth for the channel's user-facing metadata lives in the
51
+ // import-light `bundled-channel-metas` module (re-exported via the SDK barrel),
52
+ // so the registry / markdown gate can read it without loading this adapter.
53
+ // `DISCORD_CHANNEL_META.id` is the same canonical `"discord"` string as
54
+ // `DISCORD_CHANNEL_ID`.
55
+ const DISCORD_META = DISCORD_CHANNEL_META;
56
+ /**
57
+ * Collect the numeric guild channel ids configured under
58
+ * `channels.discord.guilds.<guildId>.channels.<channelId>` so the permission
59
+ * audit knows which channels to check. Non-numeric keys are passed through too —
60
+ * the audit reports them as unresolved. Returns [] when none are configured.
61
+ */
62
+ export function collectConfiguredDiscordChannelIds(cfg) {
63
+ const slot = cfg.channels?.[DISCORD_CHANNEL_ID];
64
+ const guilds = slot && typeof slot === "object" ? slot.guilds : undefined;
65
+ if (!guilds || typeof guilds !== "object")
66
+ return [];
67
+ const ids = new Set();
68
+ for (const guildValue of Object.values(guilds)) {
69
+ if (!guildValue || typeof guildValue !== "object")
70
+ continue;
71
+ const channels = guildValue.channels;
72
+ if (!channels || typeof channels !== "object")
73
+ continue;
74
+ for (const channelKey of Object.keys(channels)) {
75
+ const key = channelKey.trim();
76
+ if (key)
77
+ ids.add(key);
78
+ }
79
+ }
80
+ return [...ids];
81
+ }
82
+ /** Build the per-account approval capability — the native component prompt + approver gate. */
83
+ function buildApprovalCapability(adapter, accountId) {
84
+ return {
85
+ async sendApprovalPrompt(params) {
86
+ // Delegate to the adapter's own native prompt (component buttons). The
87
+ // adapter throws when the approval id can't be encoded, so the router
88
+ // falls back to its text prompt — mirror that here.
89
+ const cap = adapter.approvalCapability?.sendApprovalPrompt;
90
+ if (!cap)
91
+ throw new Error("discord adapter has no approval prompt");
92
+ await cap({ ...params, accountId });
93
+ },
94
+ authorizeApprover(p) {
95
+ const cap = adapter.approvalCapability?.authorizeApprover;
96
+ if (!cap)
97
+ return { authorized: true };
98
+ return cap(p);
99
+ },
100
+ };
101
+ }
102
+ /** Construct the plugin instance, capturing per-account runtime state in closure. */
103
+ export function createDiscordPlugin(deps) {
104
+ const accountRuntimes = new Map();
105
+ const startAccount = async (ctx) => {
106
+ const accountId = ctx.accountId || DISCORD_DEFAULT_ACCOUNT_ID;
107
+ // Re-entrant start (the plugin-manager's restart loop) — stop the prior
108
+ // adapter, then build fresh.
109
+ const existing = accountRuntimes.get(accountId);
110
+ if (existing) {
111
+ try {
112
+ await existing.adapter.stop();
113
+ }
114
+ catch {
115
+ /* best-effort */
116
+ }
117
+ try {
118
+ existing.abort.abort("restart");
119
+ }
120
+ catch {
121
+ /* best-effort */
122
+ }
123
+ removeChannelApprovalDispatcher(DISCORD_CHANNEL_ID, accountId);
124
+ accountRuntimes.delete(accountId);
125
+ }
126
+ const cfg = deps.loadConfig();
127
+ const factory = deps.adapterFactory ?? defaultDiscordAdapterFactory;
128
+ const adapter = factory({ accountId });
129
+ // Per-account abort derived from the gateway's parent abort.
130
+ const accountAbort = new AbortController();
131
+ const parent = ctx.signal;
132
+ if (parent) {
133
+ if (parent.aborted)
134
+ accountAbort.abort();
135
+ else
136
+ parent.addEventListener("abort", () => accountAbort.abort(), { once: true });
137
+ }
138
+ const pipelineRunTurn = (turn) => deps.runTurn(turn);
139
+ // Bundled channel commands so `/help` etc. work on the multi-account path.
140
+ const commandMap = new Map();
141
+ for (const c of buildBundledCommands(adapter)) {
142
+ commandMap.set(c.name.toLowerCase(), c);
143
+ }
144
+ const pipeline = createInboundPipelineContext({
145
+ adapter,
146
+ config: cfg,
147
+ agentId: deps.defaultAgentId,
148
+ runTurn: pipelineRunTurn,
149
+ commandMap,
150
+ parentAbort: accountAbort.signal,
151
+ });
152
+ const startCtx = {
153
+ signal: accountAbort.signal,
154
+ log: (msg, meta) => log.info(`[${accountId}] ${msg}`, meta),
155
+ onInbound: async (msg) => {
156
+ // Re-read the active config per inbound so policy edits land without
157
+ // restarting the bot. Stamp the accountId so the shared pipeline keys
158
+ // ACL + approval-route per account.
159
+ pipeline.config = deps.loadConfig();
160
+ const stamped = msg.accountId ? msg : { ...msg, accountId };
161
+ await runChannelInboundPipeline(pipeline, stamped);
162
+ },
163
+ };
164
+ try {
165
+ await adapter.start(startCtx);
166
+ accountRuntimes.set(accountId, { adapter, pipeline, abort: accountAbort });
167
+ // Per-account approval dispatcher — native component prompt + per-account
168
+ // routing. Without this an exec-gate prompt from a turn on (discord, labs)
169
+ // would fall through to the channel default.
170
+ registerChannelApprovalDispatcher(DISCORD_CHANNEL_ID, accountId, {
171
+ sendText: (conversationId, text, opts) => adapter.sendText(conversationId, text, { ...(opts ?? {}), accountId }),
172
+ prettyName: "Discord",
173
+ approvalCapability: buildApprovalCapability(adapter, accountId),
174
+ getApprovalContext: () => ({ runtime: ctx.runtime, cfg: deps.loadConfig() }),
175
+ });
176
+ log.info("discord account started", { accountId });
177
+ }
178
+ catch (err) {
179
+ log.warn("discord account failed to start", {
180
+ accountId,
181
+ error: err instanceof Error ? err.message : String(err),
182
+ });
183
+ throw err;
184
+ }
185
+ };
186
+ const stopAccount = async (ctx) => {
187
+ const runtime = accountRuntimes.get(ctx.accountId);
188
+ if (!runtime)
189
+ return;
190
+ accountRuntimes.delete(ctx.accountId);
191
+ // Drop the per-account dispatcher BEFORE adapter.stop() so a late in-flight
192
+ // bridge can't ask a torn-down bot to act.
193
+ removeChannelApprovalDispatcher(DISCORD_CHANNEL_ID, ctx.accountId);
194
+ try {
195
+ runtime.abort.abort("stop-requested");
196
+ }
197
+ catch {
198
+ /* best-effort */
199
+ }
200
+ // Clear pending debounce slots so a flush can't fire after stop.
201
+ for (const slot of runtime.pipeline.pendingDispatches.values())
202
+ clearTimeout(slot.timer);
203
+ runtime.pipeline.pendingDispatches.clear();
204
+ try {
205
+ await runtime.adapter.stop();
206
+ }
207
+ catch (err) {
208
+ log.warn("discord account stop threw", {
209
+ accountId: ctx.accountId,
210
+ error: err instanceof Error ? err.message : String(err),
211
+ });
212
+ }
213
+ };
214
+ const logoutAccount = async (ctx) => {
215
+ try {
216
+ await stopAccount(ctx);
217
+ return { ok: true };
218
+ }
219
+ catch (err) {
220
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
221
+ }
222
+ };
223
+ return {
224
+ id: DISCORD_CHANNEL_ID,
225
+ meta: DISCORD_META,
226
+ capabilities: DISCORD_CAPABILITIES,
227
+ startedAccountIds: () => [...accountRuntimes.keys()],
228
+ getAdapter: (accountId) => accountRuntimes.get(accountId)?.adapter,
229
+ probeAccount: async (accountId, cfg) => {
230
+ const token = resolveDiscordBotToken(cfg, accountId);
231
+ const result = await probeDiscord({ token });
232
+ // Channel-permission audit over any configured guild channels (Phase 5).
233
+ // Best-effort: skipped when the token / probe failed (nothing to check
234
+ // against), or when no channels are configured.
235
+ let permissionAudit;
236
+ const channelIds = collectConfiguredDiscordChannelIds(cfg);
237
+ if (result.ok && token && channelIds.length > 0) {
238
+ try {
239
+ permissionAudit = await auditDiscordChannelPermissions(token, channelIds);
240
+ }
241
+ catch {
242
+ /* never fail the probe on the audit */
243
+ }
244
+ }
245
+ // Surface the started adapter's liveness signal alongside the /users/@me
246
+ // reachability check (observability only — never changes `ok`).
247
+ const live = accountRuntimes.get(accountId)?.adapter;
248
+ const lastEventAt = live && typeof live.lastEventAt === "function" ? live.lastEventAt() : undefined;
249
+ return {
250
+ ...result,
251
+ ...(lastEventAt !== undefined ? { lastEventAt } : {}),
252
+ ...(permissionAudit ? { permissionAudit } : {}),
253
+ };
254
+ },
255
+ config: {
256
+ listAccountIds: (cfg) => listDiscordAccountIds(cfg),
257
+ resolveAccount: (cfg, accountId) => resolveDiscordAccount(cfg, accountId ?? undefined),
258
+ defaultAccountId: () => DISCORD_DEFAULT_ACCOUNT_ID,
259
+ isEnabled: (account) => account.enabled,
260
+ },
261
+ gateway: {
262
+ startAccount,
263
+ stopAccount,
264
+ logoutAccount,
265
+ },
266
+ outbound: {
267
+ sendText: async (params) => {
268
+ const accountId = params.target.accountId || DISCORD_DEFAULT_ACCOUNT_ID;
269
+ const runtime = accountRuntimes.get(accountId);
270
+ if (!runtime) {
271
+ return { ok: false, error: `discord account "${accountId}" is not running` };
272
+ }
273
+ try {
274
+ const sent = await runtime.adapter.sendText(params.target.to, params.text, {
275
+ accountId,
276
+ ...(params.target.threadId !== undefined ? { threadId: params.target.threadId } : {}),
277
+ });
278
+ return {
279
+ ok: true,
280
+ ...(sent && typeof sent === "object" && sent.messageId !== undefined ? { messageId: sent.messageId } : {}),
281
+ };
282
+ }
283
+ catch (err) {
284
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
285
+ }
286
+ },
287
+ sendMedia: async (params) => {
288
+ const accountId = params.target.accountId || DISCORD_DEFAULT_ACCOUNT_ID;
289
+ const runtime = accountRuntimes.get(accountId);
290
+ if (!runtime || !runtime.adapter.sendMedia) {
291
+ return { ok: false, error: `discord account "${accountId}" cannot send media right now` };
292
+ }
293
+ try {
294
+ await runtime.adapter.sendMedia(params.target.to, {
295
+ kind: params.mediaType ?? "document",
296
+ path: params.mediaUrl,
297
+ ...(params.caption !== undefined ? { caption: params.caption } : {}),
298
+ });
299
+ return { ok: true };
300
+ }
301
+ catch (err) {
302
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
303
+ }
304
+ },
305
+ sendReaction: async (params) => {
306
+ const accountId = params.target.accountId || DISCORD_DEFAULT_ACCOUNT_ID;
307
+ const runtime = accountRuntimes.get(accountId);
308
+ if (!runtime || !runtime.adapter.react) {
309
+ return { ok: false, error: `discord account "${accountId}" cannot react right now` };
310
+ }
311
+ try {
312
+ await runtime.adapter.react(params.target.to, params.messageId, params.emoji);
313
+ return { ok: true };
314
+ }
315
+ catch (err) {
316
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
317
+ }
318
+ },
319
+ },
320
+ actions: {
321
+ handleAction: async (params) => {
322
+ const accountId = params.accountId || params.target.accountId || DISCORD_DEFAULT_ACCOUNT_ID;
323
+ const runtime = accountRuntimes.get(accountId);
324
+ if (!runtime || !runtime.adapter.handleAction) {
325
+ return { ok: false, error: `discord account "${accountId}" cannot perform message actions` };
326
+ }
327
+ return runtime.adapter.handleAction({
328
+ conversationId: params.target.to,
329
+ action: params.action,
330
+ accountId,
331
+ ...(params.signal ? { signal: params.signal } : {}),
332
+ });
333
+ },
334
+ },
335
+ secrets: {
336
+ secretTargetRegistryEntries: [
337
+ { path: "channels.discord.botToken", description: "Discord bot token (single-account)" },
338
+ { path: "channels.discord.accounts.*.botToken", description: "Discord bot token (per account)" },
339
+ ],
340
+ },
341
+ // Supplementary security audit (Phase 5): warn on name-based (mutable)
342
+ // allow-list entries. Consumed by `brigade doctor` via the central
343
+ // `channel-security-registry.ts` collector.
344
+ security: {
345
+ collectAuditFindings: (ctx) => collectDiscordSecurityAuditFindings({ cfg: ctx.sourceConfig, accountId: ctx.accountId }),
346
+ },
347
+ // Structured status rollup (Phase 5): intent + permission issues. The
348
+ // central status surface stashes the probe/audit on each account snapshot
349
+ // (under `probe` / `audit`); this adapts those into Discord status issues.
350
+ status: {
351
+ collectStatusIssues: (accounts) => collectDiscordStatusIssues(accounts.map((snap) => {
352
+ const s = snap;
353
+ return {
354
+ accountId: typeof s.accountId === "string" ? s.accountId : "",
355
+ ...(s.probe ? { probe: s.probe } : {}),
356
+ // The audit may ride on the probe (probeAccount attaches it) or be
357
+ // stashed directly on the snapshot.
358
+ ...(s.probe?.permissionAudit
359
+ ? { audit: s.probe.permissionAudit }
360
+ : s.audit
361
+ ? { audit: s.audit }
362
+ : {}),
363
+ };
364
+ })),
365
+ },
366
+ };
367
+ }
368
+ /** Default adapter factory — threads the per-account scope. */
369
+ function defaultDiscordAdapterFactory(args) {
370
+ return createDiscordAdapter({ accountId: args.accountId });
371
+ }
372
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../../src/agents/channels/discord/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Discord status / doctor probe — a lightweight `GET /users/@me` reachability
3
+ * check.
4
+ *
5
+ * Discord is token-based and stateless on disk (the Gateway keeps no local creds
6
+ * the status command can stat), so "is this channel actually working?" can only
7
+ * be answered by asking Discord. This probe does the cheapest possible call — a
8
+ * single `GET /users/@me` over plain HTTPS (no `discord.js`, no gateway socket)
9
+ * with the `Authorization: Bot <token>` header — and reports the bot's identity
10
+ * so `brigade channels status` and `brigade doctor` can show real Discord health.
11
+ *
12
+ * It deliberately does NOT import `discord.js`: a status check must stay fast +
13
+ * dependency-light, and `/users/@me` is a trivial GET. The bot token is never
14
+ * logged; it rides in the `Authorization` header which is built locally and
15
+ * discarded.
16
+ *
17
+ * Returns a structured result the caller renders; never throws — a network
18
+ * failure / invalid token surfaces as `{ ok: false, error }` so the status
19
+ * command degrades gracefully instead of crashing. Discord mirror of
20
+ * `slack/probe.ts`.
21
+ */
22
+ /** Decoded state of a privileged gateway intent. */
23
+ export type MessageContentIntentState = "enabled" | "limited" | "disabled";
24
+ /** Alias — every privileged intent decodes to the same tri-state. */
25
+ export type PrivilegedIntentState = MessageContentIntentState;
26
+ /** All three privileged intents decoded from the application flags. */
27
+ export interface DiscordPrivilegedIntents {
28
+ messageContent: PrivilegedIntentState;
29
+ guildMembers: PrivilegedIntentState;
30
+ presence: PrivilegedIntentState;
31
+ }
32
+ /**
33
+ * Decode the MESSAGE CONTENT intent state from an application `flags` bitfield.
34
+ * `enabled` (full), `limited` (limited rollout — still reads content), or
35
+ * `disabled` (neither bit set). Returns `undefined` when `flags` isn't a number
36
+ * (the flags fetch was skipped / failed) so callers don't warn on missing data.
37
+ */
38
+ export declare function decodeMessageContentIntent(flags: unknown): MessageContentIntentState | undefined;
39
+ /**
40
+ * Decode ALL three privileged gateway intents (message content, guild members,
41
+ * presence) from an application `flags` bitfield. Returns `undefined` when
42
+ * `flags` isn't a finite number (the flags fetch was skipped / failed) so
43
+ * callers don't warn on missing data.
44
+ */
45
+ export declare function decodePrivilegedIntents(flags: unknown): DiscordPrivilegedIntents | undefined;
46
+ /** Operator-facing warning surfaced when the MESSAGE CONTENT intent is disabled. */
47
+ export declare const MESSAGE_CONTENT_DISABLED_WARNING = "Enable the MESSAGE CONTENT intent in the Discord Developer Portal \u2014 the bot can't read channel messages without it.";
48
+ /** Bot identity surfaced by the probe (from `/users/@me`). */
49
+ export interface DiscordProbeBot {
50
+ /** Bot user id (snowflake). */
51
+ id?: string;
52
+ /** Bot username. */
53
+ name?: string;
54
+ /** Discriminator (legacy `#0001`) when present. */
55
+ discriminator?: string;
56
+ }
57
+ /** Structured probe result. `ok` true ⇒ token valid + Discord reachable. */
58
+ export interface DiscordProbeResult {
59
+ ok: boolean;
60
+ /** HTTP status of the `/users/@me` call, when one came back. */
61
+ status?: number;
62
+ /** Operator-facing error line when `ok` is false. */
63
+ error?: string;
64
+ /** Round-trip time in ms. */
65
+ elapsedMs: number;
66
+ /** Bot identity (populated on success). */
67
+ bot?: DiscordProbeBot;
68
+ /**
69
+ * Epoch ms of the most recent inbound event seen by the STARTED adapter for
70
+ * this account, when one is running (liveness signal — `/users/@me` proves the
71
+ * token but not that the Gateway stream is flowing). `null` when no inbound has
72
+ * arrived yet; `undefined` when the probe couldn't consult a live adapter (a
73
+ * cold status check before the channel started). Observability only — a stale
74
+ * value never means "unhealthy" (a quiet channel is idle, not down).
75
+ */
76
+ lastEventAt?: number | null;
77
+ /**
78
+ * State of the privileged MESSAGE CONTENT intent, decoded from the bot's
79
+ * application flags. `"disabled"` is the #1 Discord footgun — the bot connects
80
+ * fine but silently can't read channel messages. `undefined` when the flags
81
+ * check was skipped or failed (best-effort; never fails the probe).
82
+ */
83
+ messageContentIntent?: MessageContentIntentState;
84
+ /**
85
+ * Operator-facing warning when {@link messageContentIntent} is `"disabled"`.
86
+ * `undefined` otherwise. The status surface renders it so the operator knows to
87
+ * flip the intent toggle.
88
+ */
89
+ messageContentWarning?: string;
90
+ /**
91
+ * Full decode of ALL three privileged gateway intents (message content, guild
92
+ * members, presence). `undefined` when the flags check was skipped / failed.
93
+ * `messageContentIntent` above is kept for back-compat; this carries the other
94
+ * two so diagnostics can flag a disabled GUILD MEMBERS / PRESENCE intent too.
95
+ */
96
+ privilegedIntents?: DiscordPrivilegedIntents;
97
+ }
98
+ export interface DiscordProbeArgs {
99
+ /** The resolved bot token. NEVER logged. */
100
+ token: string;
101
+ /** Injectable fetch (defaults to global fetch) — lets tests stub the call. */
102
+ fetchImpl?: typeof fetch;
103
+ /** Probe timeout in ms (default 8s). */
104
+ timeoutMs?: number;
105
+ }
106
+ /**
107
+ * Run a `GET /users/@me` probe. Resolves to a structured result describing
108
+ * whether the token is valid + Discord is reachable, plus the bot identity. On
109
+ * success it ALSO best-effort decodes the privileged MESSAGE CONTENT intent
110
+ * (`/oauth2/applications/@me` → `flags`) and surfaces a warning when it's
111
+ * disabled — without that intent the bot connects but can't read channel
112
+ * messages. Never rejects.
113
+ */
114
+ export declare function probeDiscord(args: DiscordProbeArgs): Promise<DiscordProbeResult>;
115
+ //# sourceMappingURL=probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/discord/probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA8BH,oDAAoD;AACpD,MAAM,MAAM,yBAAyB,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAC3E,qEAAqE;AACrE,MAAM,MAAM,qBAAqB,GAAG,yBAAyB,CAAC;AAE9D,uEAAuE;AACvE,MAAM,WAAW,wBAAwB;IACxC,cAAc,EAAE,qBAAqB,CAAC;IACtC,YAAY,EAAE,qBAAqB,CAAC;IACpC,QAAQ,EAAE,qBAAqB,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,GAAG,SAAS,CAKhG;AASD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,GAAG,SAAS,CAO5F;AAED,oFAAoF;AACpF,eAAO,MAAM,gCAAgC,6HACyE,CAAC;AAEvH,8DAA8D;AAC9D,MAAM,WAAW,eAAe;IAC/B,+BAA+B;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,4EAA4E;AAC5E,MAAM,WAAW,kBAAkB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,yBAAyB,CAAC;IACjD;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;CAC7C;AAED,MAAM,WAAW,gBAAgB;IAChC,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AA2BD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA0EtF"}