@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.
- package/dist/agents/agent-loop.d.ts.map +1 -1
- package/dist/agents/agent-loop.js +51 -1
- package/dist/agents/agent-loop.js.map +1 -1
- package/dist/agents/channels/bundled-channel-metas.d.ts +4 -0
- package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -1
- package/dist/agents/channels/bundled-channel-metas.js +22 -0
- package/dist/agents/channels/bundled-channel-metas.js.map +1 -1
- package/dist/agents/channels/discord/account-config.d.ts +117 -0
- package/dist/agents/channels/discord/account-config.d.ts.map +1 -0
- package/dist/agents/channels/discord/account-config.js +260 -0
- package/dist/agents/channels/discord/account-config.js.map +1 -0
- package/dist/agents/channels/discord/adapter.d.ts +56 -0
- package/dist/agents/channels/discord/adapter.d.ts.map +1 -0
- package/dist/agents/channels/discord/adapter.js +526 -0
- package/dist/agents/channels/discord/adapter.js.map +1 -0
- package/dist/agents/channels/discord/approval-authorize.d.ts +43 -0
- package/dist/agents/channels/discord/approval-authorize.d.ts.map +1 -0
- package/dist/agents/channels/discord/approval-authorize.js +71 -0
- package/dist/agents/channels/discord/approval-authorize.js.map +1 -0
- package/dist/agents/channels/discord/approval-native.d.ts +68 -0
- package/dist/agents/channels/discord/approval-native.d.ts.map +1 -0
- package/dist/agents/channels/discord/approval-native.js +81 -0
- package/dist/agents/channels/discord/approval-native.js.map +1 -0
- package/dist/agents/channels/discord/command-menu.d.ts +49 -0
- package/dist/agents/channels/discord/command-menu.d.ts.map +1 -0
- package/dist/agents/channels/discord/command-menu.js +73 -0
- package/dist/agents/channels/discord/command-menu.js.map +1 -0
- package/dist/agents/channels/discord/components.d.ts +97 -0
- package/dist/agents/channels/discord/components.d.ts.map +1 -0
- package/dist/agents/channels/discord/components.js +131 -0
- package/dist/agents/channels/discord/components.js.map +1 -0
- package/dist/agents/channels/discord/connection.d.ts +387 -0
- package/dist/agents/channels/discord/connection.d.ts.map +1 -0
- package/dist/agents/channels/discord/connection.js +786 -0
- package/dist/agents/channels/discord/connection.js.map +1 -0
- package/dist/agents/channels/discord/draft-stream.d.ts +92 -0
- package/dist/agents/channels/discord/draft-stream.d.ts.map +1 -0
- package/dist/agents/channels/discord/draft-stream.js +213 -0
- package/dist/agents/channels/discord/draft-stream.js.map +1 -0
- package/dist/agents/channels/discord/format.d.ts +55 -0
- package/dist/agents/channels/discord/format.d.ts.map +1 -0
- package/dist/agents/channels/discord/format.js +247 -0
- package/dist/agents/channels/discord/format.js.map +1 -0
- package/dist/agents/channels/discord/inbound-extras.d.ts +220 -0
- package/dist/agents/channels/discord/inbound-extras.d.ts.map +1 -0
- package/dist/agents/channels/discord/inbound-extras.js +351 -0
- package/dist/agents/channels/discord/inbound-extras.js.map +1 -0
- package/dist/agents/channels/discord/index.d.ts +14 -0
- package/dist/agents/channels/discord/index.d.ts.map +1 -0
- package/dist/agents/channels/discord/index.js +14 -0
- package/dist/agents/channels/discord/index.js.map +1 -0
- package/dist/agents/channels/discord/media.d.ts +85 -0
- package/dist/agents/channels/discord/media.d.ts.map +1 -0
- package/dist/agents/channels/discord/media.js +242 -0
- package/dist/agents/channels/discord/media.js.map +1 -0
- package/dist/agents/channels/discord/module.d.ts +15 -0
- package/dist/agents/channels/discord/module.d.ts.map +1 -0
- package/dist/agents/channels/discord/module.js +22 -0
- package/dist/agents/channels/discord/module.js.map +1 -0
- package/dist/agents/channels/discord/plugin.d.ts +73 -0
- package/dist/agents/channels/discord/plugin.d.ts.map +1 -0
- package/dist/agents/channels/discord/plugin.js +303 -0
- package/dist/agents/channels/discord/plugin.js.map +1 -0
- package/dist/agents/channels/discord/probe.d.ts +93 -0
- package/dist/agents/channels/discord/probe.d.ts.map +1 -0
- package/dist/agents/channels/discord/probe.js +158 -0
- package/dist/agents/channels/discord/probe.js.map +1 -0
- package/dist/agents/channels/discord/reasoning-lane.d.ts +42 -0
- package/dist/agents/channels/discord/reasoning-lane.d.ts.map +1 -0
- package/dist/agents/channels/discord/reasoning-lane.js +68 -0
- package/dist/agents/channels/discord/reasoning-lane.js.map +1 -0
- package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
- package/dist/agents/channels/inbound-pipeline.js +65 -7
- package/dist/agents/channels/inbound-pipeline.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 +4 -0
- package/dist/agents/channels/sdk.d.ts.map +1 -1
- package/dist/agents/channels/sdk.js +4 -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 +10 -0
- package/dist/agents/extensions/modules/index.js.map +1 -1
- package/dist/agents/tools/cron-tool.d.ts.map +1 -1
- package/dist/agents/tools/cron-tool.js +4 -1
- package/dist/agents/tools/cron-tool.js.map +1 -1
- package/dist/buildstamp.json +1 -1
- package/dist/core/auth-bridge.d.ts +1 -0
- package/dist/core/auth-bridge.d.ts.map +1 -1
- package/dist/core/auth-bridge.js +46 -1
- package/dist/core/auth-bridge.js.map +1 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +40 -5
- package/dist/core/server.js.map +1 -1
- package/dist/cron/isolated-agent/run-executor.d.ts +11 -0
- package/dist/cron/isolated-agent/run-executor.d.ts.map +1 -1
- package/dist/cron/isolated-agent/run-executor.js +20 -4
- package/dist/cron/isolated-agent/run-executor.js.map +1 -1
- package/dist/cron/types.d.ts +8 -0
- package/dist/cron/types.d.ts.map +1 -1
- package/dist/system-prompt/assembler.d.ts.map +1 -1
- package/dist/system-prompt/assembler.js +4 -2
- package/dist/system-prompt/assembler.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,303 @@
|
|
|
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
|
+
const log = createSubsystemLogger("channels/discord/plugin");
|
|
47
|
+
// Single source of truth for the channel's user-facing metadata lives in the
|
|
48
|
+
// import-light `bundled-channel-metas` module (re-exported via the SDK barrel),
|
|
49
|
+
// so the registry / markdown gate can read it without loading this adapter.
|
|
50
|
+
// `DISCORD_CHANNEL_META.id` is the same canonical `"discord"` string as
|
|
51
|
+
// `DISCORD_CHANNEL_ID`.
|
|
52
|
+
const DISCORD_META = DISCORD_CHANNEL_META;
|
|
53
|
+
/** Build the per-account approval capability — the native component prompt + approver gate. */
|
|
54
|
+
function buildApprovalCapability(adapter, accountId) {
|
|
55
|
+
return {
|
|
56
|
+
async sendApprovalPrompt(params) {
|
|
57
|
+
// Delegate to the adapter's own native prompt (component buttons). The
|
|
58
|
+
// adapter throws when the approval id can't be encoded, so the router
|
|
59
|
+
// falls back to its text prompt — mirror that here.
|
|
60
|
+
const cap = adapter.approvalCapability?.sendApprovalPrompt;
|
|
61
|
+
if (!cap)
|
|
62
|
+
throw new Error("discord adapter has no approval prompt");
|
|
63
|
+
await cap({ ...params, accountId });
|
|
64
|
+
},
|
|
65
|
+
authorizeApprover(p) {
|
|
66
|
+
const cap = adapter.approvalCapability?.authorizeApprover;
|
|
67
|
+
if (!cap)
|
|
68
|
+
return { authorized: true };
|
|
69
|
+
return cap(p);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** Construct the plugin instance, capturing per-account runtime state in closure. */
|
|
74
|
+
export function createDiscordPlugin(deps) {
|
|
75
|
+
const accountRuntimes = new Map();
|
|
76
|
+
const startAccount = async (ctx) => {
|
|
77
|
+
const accountId = ctx.accountId || DISCORD_DEFAULT_ACCOUNT_ID;
|
|
78
|
+
// Re-entrant start (the plugin-manager's restart loop) — stop the prior
|
|
79
|
+
// adapter, then build fresh.
|
|
80
|
+
const existing = accountRuntimes.get(accountId);
|
|
81
|
+
if (existing) {
|
|
82
|
+
try {
|
|
83
|
+
await existing.adapter.stop();
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
/* best-effort */
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
existing.abort.abort("restart");
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
/* best-effort */
|
|
93
|
+
}
|
|
94
|
+
removeChannelApprovalDispatcher(DISCORD_CHANNEL_ID, accountId);
|
|
95
|
+
accountRuntimes.delete(accountId);
|
|
96
|
+
}
|
|
97
|
+
const cfg = deps.loadConfig();
|
|
98
|
+
const factory = deps.adapterFactory ?? defaultDiscordAdapterFactory;
|
|
99
|
+
const adapter = factory({ accountId });
|
|
100
|
+
// Per-account abort derived from the gateway's parent abort.
|
|
101
|
+
const accountAbort = new AbortController();
|
|
102
|
+
const parent = ctx.signal;
|
|
103
|
+
if (parent) {
|
|
104
|
+
if (parent.aborted)
|
|
105
|
+
accountAbort.abort();
|
|
106
|
+
else
|
|
107
|
+
parent.addEventListener("abort", () => accountAbort.abort(), { once: true });
|
|
108
|
+
}
|
|
109
|
+
const pipelineRunTurn = (turn) => deps.runTurn(turn);
|
|
110
|
+
// Bundled channel commands so `/help` etc. work on the multi-account path.
|
|
111
|
+
const commandMap = new Map();
|
|
112
|
+
for (const c of buildBundledCommands(adapter)) {
|
|
113
|
+
commandMap.set(c.name.toLowerCase(), c);
|
|
114
|
+
}
|
|
115
|
+
const pipeline = createInboundPipelineContext({
|
|
116
|
+
adapter,
|
|
117
|
+
config: cfg,
|
|
118
|
+
agentId: deps.defaultAgentId,
|
|
119
|
+
runTurn: pipelineRunTurn,
|
|
120
|
+
commandMap,
|
|
121
|
+
parentAbort: accountAbort.signal,
|
|
122
|
+
});
|
|
123
|
+
const startCtx = {
|
|
124
|
+
signal: accountAbort.signal,
|
|
125
|
+
log: (msg, meta) => log.info(`[${accountId}] ${msg}`, meta),
|
|
126
|
+
onInbound: async (msg) => {
|
|
127
|
+
// Re-read the active config per inbound so policy edits land without
|
|
128
|
+
// restarting the bot. Stamp the accountId so the shared pipeline keys
|
|
129
|
+
// ACL + approval-route per account.
|
|
130
|
+
pipeline.config = deps.loadConfig();
|
|
131
|
+
const stamped = msg.accountId ? msg : { ...msg, accountId };
|
|
132
|
+
await runChannelInboundPipeline(pipeline, stamped);
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
try {
|
|
136
|
+
await adapter.start(startCtx);
|
|
137
|
+
accountRuntimes.set(accountId, { adapter, pipeline, abort: accountAbort });
|
|
138
|
+
// Per-account approval dispatcher — native component prompt + per-account
|
|
139
|
+
// routing. Without this an exec-gate prompt from a turn on (discord, labs)
|
|
140
|
+
// would fall through to the channel default.
|
|
141
|
+
registerChannelApprovalDispatcher(DISCORD_CHANNEL_ID, accountId, {
|
|
142
|
+
sendText: (conversationId, text, opts) => adapter.sendText(conversationId, text, { ...(opts ?? {}), accountId }),
|
|
143
|
+
prettyName: "Discord",
|
|
144
|
+
approvalCapability: buildApprovalCapability(adapter, accountId),
|
|
145
|
+
getApprovalContext: () => ({ runtime: ctx.runtime, cfg: deps.loadConfig() }),
|
|
146
|
+
});
|
|
147
|
+
log.info("discord account started", { accountId });
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
log.warn("discord account failed to start", {
|
|
151
|
+
accountId,
|
|
152
|
+
error: err instanceof Error ? err.message : String(err),
|
|
153
|
+
});
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
const stopAccount = async (ctx) => {
|
|
158
|
+
const runtime = accountRuntimes.get(ctx.accountId);
|
|
159
|
+
if (!runtime)
|
|
160
|
+
return;
|
|
161
|
+
accountRuntimes.delete(ctx.accountId);
|
|
162
|
+
// Drop the per-account dispatcher BEFORE adapter.stop() so a late in-flight
|
|
163
|
+
// bridge can't ask a torn-down bot to act.
|
|
164
|
+
removeChannelApprovalDispatcher(DISCORD_CHANNEL_ID, ctx.accountId);
|
|
165
|
+
try {
|
|
166
|
+
runtime.abort.abort("stop-requested");
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
/* best-effort */
|
|
170
|
+
}
|
|
171
|
+
// Clear pending debounce slots so a flush can't fire after stop.
|
|
172
|
+
for (const slot of runtime.pipeline.pendingDispatches.values())
|
|
173
|
+
clearTimeout(slot.timer);
|
|
174
|
+
runtime.pipeline.pendingDispatches.clear();
|
|
175
|
+
try {
|
|
176
|
+
await runtime.adapter.stop();
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
log.warn("discord account stop threw", {
|
|
180
|
+
accountId: ctx.accountId,
|
|
181
|
+
error: err instanceof Error ? err.message : String(err),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const logoutAccount = async (ctx) => {
|
|
186
|
+
try {
|
|
187
|
+
await stopAccount(ctx);
|
|
188
|
+
return { ok: true };
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
return {
|
|
195
|
+
id: DISCORD_CHANNEL_ID,
|
|
196
|
+
meta: DISCORD_META,
|
|
197
|
+
capabilities: DISCORD_CAPABILITIES,
|
|
198
|
+
startedAccountIds: () => [...accountRuntimes.keys()],
|
|
199
|
+
getAdapter: (accountId) => accountRuntimes.get(accountId)?.adapter,
|
|
200
|
+
probeAccount: async (accountId, cfg) => {
|
|
201
|
+
const token = resolveDiscordBotToken(cfg, accountId);
|
|
202
|
+
const result = await probeDiscord({ token });
|
|
203
|
+
// Surface the started adapter's liveness signal alongside the /users/@me
|
|
204
|
+
// reachability check (observability only — never changes `ok`).
|
|
205
|
+
const live = accountRuntimes.get(accountId)?.adapter;
|
|
206
|
+
if (live && typeof live.lastEventAt === "function") {
|
|
207
|
+
return { ...result, lastEventAt: live.lastEventAt() };
|
|
208
|
+
}
|
|
209
|
+
return result;
|
|
210
|
+
},
|
|
211
|
+
config: {
|
|
212
|
+
listAccountIds: (cfg) => listDiscordAccountIds(cfg),
|
|
213
|
+
resolveAccount: (cfg, accountId) => resolveDiscordAccount(cfg, accountId ?? undefined),
|
|
214
|
+
defaultAccountId: () => DISCORD_DEFAULT_ACCOUNT_ID,
|
|
215
|
+
isEnabled: (account) => account.enabled,
|
|
216
|
+
},
|
|
217
|
+
gateway: {
|
|
218
|
+
startAccount,
|
|
219
|
+
stopAccount,
|
|
220
|
+
logoutAccount,
|
|
221
|
+
},
|
|
222
|
+
outbound: {
|
|
223
|
+
sendText: async (params) => {
|
|
224
|
+
const accountId = params.target.accountId || DISCORD_DEFAULT_ACCOUNT_ID;
|
|
225
|
+
const runtime = accountRuntimes.get(accountId);
|
|
226
|
+
if (!runtime) {
|
|
227
|
+
return { ok: false, error: `discord account "${accountId}" is not running` };
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const sent = await runtime.adapter.sendText(params.target.to, params.text, {
|
|
231
|
+
accountId,
|
|
232
|
+
...(params.target.threadId !== undefined ? { threadId: params.target.threadId } : {}),
|
|
233
|
+
});
|
|
234
|
+
return {
|
|
235
|
+
ok: true,
|
|
236
|
+
...(sent && typeof sent === "object" && sent.messageId !== undefined ? { messageId: sent.messageId } : {}),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
sendMedia: async (params) => {
|
|
244
|
+
const accountId = params.target.accountId || DISCORD_DEFAULT_ACCOUNT_ID;
|
|
245
|
+
const runtime = accountRuntimes.get(accountId);
|
|
246
|
+
if (!runtime || !runtime.adapter.sendMedia) {
|
|
247
|
+
return { ok: false, error: `discord account "${accountId}" cannot send media right now` };
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
await runtime.adapter.sendMedia(params.target.to, {
|
|
251
|
+
kind: params.mediaType ?? "document",
|
|
252
|
+
path: params.mediaUrl,
|
|
253
|
+
...(params.caption !== undefined ? { caption: params.caption } : {}),
|
|
254
|
+
});
|
|
255
|
+
return { ok: true };
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
sendReaction: async (params) => {
|
|
262
|
+
const accountId = params.target.accountId || DISCORD_DEFAULT_ACCOUNT_ID;
|
|
263
|
+
const runtime = accountRuntimes.get(accountId);
|
|
264
|
+
if (!runtime || !runtime.adapter.react) {
|
|
265
|
+
return { ok: false, error: `discord account "${accountId}" cannot react right now` };
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
await runtime.adapter.react(params.target.to, params.messageId, params.emoji);
|
|
269
|
+
return { ok: true };
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
actions: {
|
|
277
|
+
handleAction: async (params) => {
|
|
278
|
+
const accountId = params.accountId || params.target.accountId || DISCORD_DEFAULT_ACCOUNT_ID;
|
|
279
|
+
const runtime = accountRuntimes.get(accountId);
|
|
280
|
+
if (!runtime || !runtime.adapter.handleAction) {
|
|
281
|
+
return { ok: false, error: `discord account "${accountId}" cannot perform message actions` };
|
|
282
|
+
}
|
|
283
|
+
return runtime.adapter.handleAction({
|
|
284
|
+
conversationId: params.target.to,
|
|
285
|
+
action: params.action,
|
|
286
|
+
accountId,
|
|
287
|
+
...(params.signal ? { signal: params.signal } : {}),
|
|
288
|
+
});
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
secrets: {
|
|
292
|
+
secretTargetRegistryEntries: [
|
|
293
|
+
{ path: "channels.discord.botToken", description: "Discord bot token (single-account)" },
|
|
294
|
+
{ path: "channels.discord.accounts.*.botToken", description: "Discord bot token (per account)" },
|
|
295
|
+
],
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/** Default adapter factory — threads the per-account scope. */
|
|
300
|
+
function defaultDiscordAdapterFactory(args) {
|
|
301
|
+
return createDiscordAdapter({ accountId: args.accountId });
|
|
302
|
+
}
|
|
303
|
+
//# 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,93 @@
|
|
|
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 the privileged MESSAGE CONTENT intent. */
|
|
23
|
+
export type MessageContentIntentState = "enabled" | "limited" | "disabled";
|
|
24
|
+
/**
|
|
25
|
+
* Decode the MESSAGE CONTENT intent state from an application `flags` bitfield.
|
|
26
|
+
* `enabled` (full), `limited` (limited rollout — still reads content), or
|
|
27
|
+
* `disabled` (neither bit set). Returns `undefined` when `flags` isn't a number
|
|
28
|
+
* (the flags fetch was skipped / failed) so callers don't warn on missing data.
|
|
29
|
+
*/
|
|
30
|
+
export declare function decodeMessageContentIntent(flags: unknown): MessageContentIntentState | undefined;
|
|
31
|
+
/** Operator-facing warning surfaced when the MESSAGE CONTENT intent is disabled. */
|
|
32
|
+
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.";
|
|
33
|
+
/** Bot identity surfaced by the probe (from `/users/@me`). */
|
|
34
|
+
export interface DiscordProbeBot {
|
|
35
|
+
/** Bot user id (snowflake). */
|
|
36
|
+
id?: string;
|
|
37
|
+
/** Bot username. */
|
|
38
|
+
name?: string;
|
|
39
|
+
/** Discriminator (legacy `#0001`) when present. */
|
|
40
|
+
discriminator?: string;
|
|
41
|
+
}
|
|
42
|
+
/** Structured probe result. `ok` true ⇒ token valid + Discord reachable. */
|
|
43
|
+
export interface DiscordProbeResult {
|
|
44
|
+
ok: boolean;
|
|
45
|
+
/** HTTP status of the `/users/@me` call, when one came back. */
|
|
46
|
+
status?: number;
|
|
47
|
+
/** Operator-facing error line when `ok` is false. */
|
|
48
|
+
error?: string;
|
|
49
|
+
/** Round-trip time in ms. */
|
|
50
|
+
elapsedMs: number;
|
|
51
|
+
/** Bot identity (populated on success). */
|
|
52
|
+
bot?: DiscordProbeBot;
|
|
53
|
+
/**
|
|
54
|
+
* Epoch ms of the most recent inbound event seen by the STARTED adapter for
|
|
55
|
+
* this account, when one is running (liveness signal — `/users/@me` proves the
|
|
56
|
+
* token but not that the Gateway stream is flowing). `null` when no inbound has
|
|
57
|
+
* arrived yet; `undefined` when the probe couldn't consult a live adapter (a
|
|
58
|
+
* cold status check before the channel started). Observability only — a stale
|
|
59
|
+
* value never means "unhealthy" (a quiet channel is idle, not down).
|
|
60
|
+
*/
|
|
61
|
+
lastEventAt?: number | null;
|
|
62
|
+
/**
|
|
63
|
+
* State of the privileged MESSAGE CONTENT intent, decoded from the bot's
|
|
64
|
+
* application flags. `"disabled"` is the #1 Discord footgun — the bot connects
|
|
65
|
+
* fine but silently can't read channel messages. `undefined` when the flags
|
|
66
|
+
* check was skipped or failed (best-effort; never fails the probe).
|
|
67
|
+
*/
|
|
68
|
+
messageContentIntent?: MessageContentIntentState;
|
|
69
|
+
/**
|
|
70
|
+
* Operator-facing warning when {@link messageContentIntent} is `"disabled"`.
|
|
71
|
+
* `undefined` otherwise. The status surface renders it so the operator knows to
|
|
72
|
+
* flip the intent toggle.
|
|
73
|
+
*/
|
|
74
|
+
messageContentWarning?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface DiscordProbeArgs {
|
|
77
|
+
/** The resolved bot token. NEVER logged. */
|
|
78
|
+
token: string;
|
|
79
|
+
/** Injectable fetch (defaults to global fetch) — lets tests stub the call. */
|
|
80
|
+
fetchImpl?: typeof fetch;
|
|
81
|
+
/** Probe timeout in ms (default 8s). */
|
|
82
|
+
timeoutMs?: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Run a `GET /users/@me` probe. Resolves to a structured result describing
|
|
86
|
+
* whether the token is valid + Discord is reachable, plus the bot identity. On
|
|
87
|
+
* success it ALSO best-effort decodes the privileged MESSAGE CONTENT intent
|
|
88
|
+
* (`/oauth2/applications/@me` → `flags`) and surfaces a warning when it's
|
|
89
|
+
* disabled — without that intent the bot connects but can't read channel
|
|
90
|
+
* messages. Never rejects.
|
|
91
|
+
*/
|
|
92
|
+
export declare function probeDiscord(args: DiscordProbeArgs): Promise<DiscordProbeResult>;
|
|
93
|
+
//# 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;AAmBH,8DAA8D;AAC9D,MAAM,MAAM,yBAAyB,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAE3E;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,GAAG,SAAS,CAKhG;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;CAC/B;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,CAwEtF"}
|
|
@@ -0,0 +1,158 @@
|
|
|
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
|
+
import { stripBotPrefix } from "./account-config.js";
|
|
23
|
+
const DISCORD_ME_URL = "https://discord.com/api/v10/users/@me";
|
|
24
|
+
/** Application-metadata endpoint — carries the `flags` bitfield we decode for the MESSAGE CONTENT intent. */
|
|
25
|
+
const DISCORD_APP_URL = "https://discord.com/api/v10/oauth2/applications/@me";
|
|
26
|
+
const DEFAULT_PROBE_TIMEOUT_MS = 8_000;
|
|
27
|
+
/**
|
|
28
|
+
* Application-flag bits that indicate the bot HAS the privileged MESSAGE CONTENT
|
|
29
|
+
* intent. `GATEWAY_MESSAGE_CONTENT` (1 << 18) is set when the intent is enabled;
|
|
30
|
+
* `GATEWAY_MESSAGE_CONTENT_LIMITED` (1 << 19) when it's available only to a
|
|
31
|
+
* limited number of servers (still readable). Neither set, with the intent
|
|
32
|
+
* requested, means it's DISABLED — the bot connects but can't read message text.
|
|
33
|
+
*/
|
|
34
|
+
const FLAG_MESSAGE_CONTENT = 1 << 18;
|
|
35
|
+
const FLAG_MESSAGE_CONTENT_LIMITED = 1 << 19;
|
|
36
|
+
/**
|
|
37
|
+
* Decode the MESSAGE CONTENT intent state from an application `flags` bitfield.
|
|
38
|
+
* `enabled` (full), `limited` (limited rollout — still reads content), or
|
|
39
|
+
* `disabled` (neither bit set). Returns `undefined` when `flags` isn't a number
|
|
40
|
+
* (the flags fetch was skipped / failed) so callers don't warn on missing data.
|
|
41
|
+
*/
|
|
42
|
+
export function decodeMessageContentIntent(flags) {
|
|
43
|
+
if (typeof flags !== "number" || !Number.isFinite(flags))
|
|
44
|
+
return undefined;
|
|
45
|
+
if ((flags & FLAG_MESSAGE_CONTENT) !== 0)
|
|
46
|
+
return "enabled";
|
|
47
|
+
if ((flags & FLAG_MESSAGE_CONTENT_LIMITED) !== 0)
|
|
48
|
+
return "limited";
|
|
49
|
+
return "disabled";
|
|
50
|
+
}
|
|
51
|
+
/** Operator-facing warning surfaced when the MESSAGE CONTENT intent is disabled. */
|
|
52
|
+
export const MESSAGE_CONTENT_DISABLED_WARNING = "Enable the MESSAGE CONTENT intent in the Discord Developer Portal — the bot can't read channel messages without it.";
|
|
53
|
+
/**
|
|
54
|
+
* Best-effort fetch of the bot's application `flags`, decoded into the MESSAGE
|
|
55
|
+
* CONTENT intent state. Runs only AFTER `/users/@me` succeeds (the token is
|
|
56
|
+
* known good). A failed fetch / non-ok / unparseable body returns `undefined` so
|
|
57
|
+
* the probe NEVER fails on it — the intent state is observability, not health.
|
|
58
|
+
*/
|
|
59
|
+
async function probeMessageContentIntent(doFetch, token, signal) {
|
|
60
|
+
try {
|
|
61
|
+
const res = await doFetch(DISCORD_APP_URL, {
|
|
62
|
+
method: "GET",
|
|
63
|
+
headers: { Authorization: `Bot ${token}`, "content-type": "application/json" },
|
|
64
|
+
signal,
|
|
65
|
+
});
|
|
66
|
+
if (!res.ok)
|
|
67
|
+
return undefined;
|
|
68
|
+
const body = (await res.json());
|
|
69
|
+
return decodeMessageContentIntent(body?.flags);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Run a `GET /users/@me` probe. Resolves to a structured result describing
|
|
77
|
+
* whether the token is valid + Discord is reachable, plus the bot identity. On
|
|
78
|
+
* success it ALSO best-effort decodes the privileged MESSAGE CONTENT intent
|
|
79
|
+
* (`/oauth2/applications/@me` → `flags`) and surfaces a warning when it's
|
|
80
|
+
* disabled — without that intent the bot connects but can't read channel
|
|
81
|
+
* messages. Never rejects.
|
|
82
|
+
*/
|
|
83
|
+
export async function probeDiscord(args) {
|
|
84
|
+
const started = Date.now();
|
|
85
|
+
const token = stripBotPrefix((args.token ?? "").trim());
|
|
86
|
+
if (!token) {
|
|
87
|
+
return { ok: false, error: "no Discord bot token configured", elapsedMs: 0 };
|
|
88
|
+
}
|
|
89
|
+
const doFetch = args.fetchImpl ?? fetch;
|
|
90
|
+
const timeoutMs = args.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
91
|
+
const controller = new AbortController();
|
|
92
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
93
|
+
if (typeof timer.unref === "function")
|
|
94
|
+
timer.unref();
|
|
95
|
+
try {
|
|
96
|
+
const res = await doFetch(DISCORD_ME_URL, {
|
|
97
|
+
method: "GET",
|
|
98
|
+
headers: {
|
|
99
|
+
Authorization: `Bot ${token}`,
|
|
100
|
+
"content-type": "application/json",
|
|
101
|
+
},
|
|
102
|
+
signal: controller.signal,
|
|
103
|
+
});
|
|
104
|
+
const elapsedMs = Date.now() - started;
|
|
105
|
+
let body = null;
|
|
106
|
+
try {
|
|
107
|
+
body = (await res.json());
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
body = null;
|
|
111
|
+
}
|
|
112
|
+
if (!res.ok) {
|
|
113
|
+
// 401 = bad/expired token; otherwise surface the status + any message.
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
status: res.status,
|
|
117
|
+
error: res.status === 401
|
|
118
|
+
? "Discord rejected the bot token — reset it in the Developer Portal and paste the fresh token."
|
|
119
|
+
: body?.message
|
|
120
|
+
? `Discord /users/@me failed (${body.message}).`
|
|
121
|
+
: `Discord /users/@me failed (HTTP ${res.status}).`,
|
|
122
|
+
elapsedMs,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// Token is good — best-effort decode the MESSAGE CONTENT intent (never fails
|
|
126
|
+
// the probe). Reuses the same abort signal/timeout window as the identity call.
|
|
127
|
+
const messageContentIntent = await probeMessageContentIntent(doFetch, token, controller.signal);
|
|
128
|
+
return {
|
|
129
|
+
ok: true,
|
|
130
|
+
status: res.status,
|
|
131
|
+
elapsedMs,
|
|
132
|
+
bot: {
|
|
133
|
+
...(typeof body?.id === "string" ? { id: body.id } : {}),
|
|
134
|
+
...(typeof body?.username === "string" ? { name: body.username } : {}),
|
|
135
|
+
...(typeof body?.discriminator === "string" && body.discriminator !== "0" ? { discriminator: body.discriminator } : {}),
|
|
136
|
+
},
|
|
137
|
+
...(messageContentIntent ? { messageContentIntent } : {}),
|
|
138
|
+
...(messageContentIntent === "disabled" ? { messageContentWarning: MESSAGE_CONTENT_DISABLED_WARNING } : {}),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
const elapsedMs = Date.now() - started;
|
|
143
|
+
const aborted = controller.signal.aborted;
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
error: aborted
|
|
147
|
+
? `Discord /users/@me timed out after ${timeoutMs}ms`
|
|
148
|
+
: err instanceof Error
|
|
149
|
+
? err.message
|
|
150
|
+
: String(err),
|
|
151
|
+
elapsedMs,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../../../src/agents/channels/discord/probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord reasoning-lane split (OPTIONAL, default OFF).
|
|
3
|
+
*
|
|
4
|
+
* Brigade strips `<think>…</think>` reasoning from every channel reply via the
|
|
5
|
+
* shared `sanitizeReplyForChannel` — recipients see only the final answer. For
|
|
6
|
+
* Discord an operator can OPT IN (config `channels.discord.surfaceReasoning:
|
|
7
|
+
* true`) to ALSO receive the reasoning trace as a separate, prefixed message.
|
|
8
|
+
*
|
|
9
|
+
* This module is the pure splitter: given the raw agent reply, it returns
|
|
10
|
+
* `{ reasoningText?, answerText }`. When surfacing is OFF the pipeline never
|
|
11
|
+
* calls this and behavior is byte-identical to today (strip + send answer). When
|
|
12
|
+
* ON, the pipeline sends `reasoningText` first (a `🧠 Reasoning:` block) then the
|
|
13
|
+
* normal sanitized answer.
|
|
14
|
+
*
|
|
15
|
+
* The answer half is computed with the SAME sanitizer the default path uses, so
|
|
16
|
+
* enabling reasoning never changes what the answer message contains — it only
|
|
17
|
+
* ADDS the reasoning message in front.
|
|
18
|
+
*
|
|
19
|
+
* Pure / deterministic / dependency-light (re-uses `sanitizeReplyForChannel`).
|
|
20
|
+
* Discord mirror of `slack/reasoning-lane.ts`.
|
|
21
|
+
*/
|
|
22
|
+
/** Prefix on the reasoning message so the recipient knows it's the trace. */
|
|
23
|
+
export declare const REASONING_PREFIX = "\uD83E\uDDE0 Reasoning:\n";
|
|
24
|
+
export interface DiscordReasoningSplit {
|
|
25
|
+
/** The extracted reasoning trace (already prefixed), or undefined when none. */
|
|
26
|
+
reasoningText?: string;
|
|
27
|
+
/** The user-facing answer (sanitized exactly as the default path produces). */
|
|
28
|
+
answerText: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Extract the concatenated text INSIDE `<think>…</think>` blocks. Handles
|
|
32
|
+
* multiple blocks and an unclosed trailing block (model truncated mid-thought).
|
|
33
|
+
* Returns "" when there's no reasoning content.
|
|
34
|
+
*/
|
|
35
|
+
export declare function extractReasoning(text: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Split a raw agent reply into an optional reasoning message + the sanitized
|
|
38
|
+
* answer. The answer is identical to `sanitizeReplyForChannel(raw)`; the
|
|
39
|
+
* reasoning is only populated when a `<think>` block carried content.
|
|
40
|
+
*/
|
|
41
|
+
export declare function splitDiscordReasoning(raw: string): DiscordReasoningSplit;
|
|
42
|
+
//# sourceMappingURL=reasoning-lane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-lane.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/discord/reasoning-lane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,8BAAoB,CAAC;AAKlD,MAAM,WAAW,qBAAqB;IACrC,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoBrD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAKxE"}
|