@spinabot/brigade 1.1.0 → 1.2.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/convex/channels.d.ts +5 -5
- package/convex/schema.d.ts +2 -2
- package/dist/agents/agent-loop.d.ts.map +1 -1
- package/dist/agents/agent-loop.js +27 -4
- package/dist/agents/agent-loop.js.map +1 -1
- package/dist/agents/channels/approval-callback-codec.d.ts +107 -0
- package/dist/agents/channels/approval-callback-codec.d.ts.map +1 -0
- package/dist/agents/channels/approval-callback-codec.js +173 -0
- package/dist/agents/channels/approval-callback-codec.js.map +1 -0
- package/dist/agents/channels/approval-router.d.ts +77 -20
- package/dist/agents/channels/approval-router.d.ts.map +1 -1
- package/dist/agents/channels/approval-router.js +163 -37
- package/dist/agents/channels/approval-router.js.map +1 -1
- package/dist/agents/channels/backoff.d.ts +55 -0
- package/dist/agents/channels/backoff.d.ts.map +1 -0
- package/dist/agents/channels/backoff.js +47 -0
- package/dist/agents/channels/backoff.js.map +1 -0
- package/dist/agents/channels/channel-secrets.d.ts +45 -0
- package/dist/agents/channels/channel-secrets.d.ts.map +1 -0
- package/dist/agents/channels/channel-secrets.js +69 -0
- package/dist/agents/channels/channel-secrets.js.map +1 -0
- package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
- package/dist/agents/channels/inbound-pipeline.js +67 -3
- package/dist/agents/channels/inbound-pipeline.js.map +1 -1
- package/dist/agents/channels/last-sent-message.d.ts +46 -0
- package/dist/agents/channels/last-sent-message.d.ts.map +1 -0
- package/dist/agents/channels/last-sent-message.js +55 -0
- package/dist/agents/channels/last-sent-message.js.map +1 -0
- package/dist/agents/channels/manager.d.ts +52 -0
- package/dist/agents/channels/manager.d.ts.map +1 -1
- package/dist/agents/channels/manager.js +141 -31
- package/dist/agents/channels/manager.js.map +1 -1
- package/dist/agents/channels/plugin-channel-manager-facade.d.ts +13 -2
- package/dist/agents/channels/plugin-channel-manager-facade.d.ts.map +1 -1
- package/dist/agents/channels/plugin-channel-manager-facade.js +21 -0
- package/dist/agents/channels/plugin-channel-manager-facade.js.map +1 -1
- package/dist/agents/channels/sdk.d.ts +426 -0
- package/dist/agents/channels/sdk.d.ts.map +1 -0
- package/dist/agents/channels/sdk.js +274 -0
- package/dist/agents/channels/sdk.js.map +1 -0
- package/dist/agents/channels/telegram/account-config.d.ts +92 -0
- package/dist/agents/channels/telegram/account-config.d.ts.map +1 -0
- package/dist/agents/channels/telegram/account-config.js +192 -0
- package/dist/agents/channels/telegram/account-config.js.map +1 -0
- package/dist/agents/channels/telegram/adapter.d.ts +79 -0
- package/dist/agents/channels/telegram/adapter.d.ts.map +1 -0
- package/dist/agents/channels/telegram/adapter.js +475 -0
- package/dist/agents/channels/telegram/adapter.js.map +1 -0
- package/dist/agents/channels/telegram/allowed-updates.d.ts +44 -0
- package/dist/agents/channels/telegram/allowed-updates.d.ts.map +1 -0
- package/dist/agents/channels/telegram/allowed-updates.js +52 -0
- package/dist/agents/channels/telegram/allowed-updates.js.map +1 -0
- package/dist/agents/channels/telegram/approval-authorize.d.ts +41 -0
- package/dist/agents/channels/telegram/approval-authorize.d.ts.map +1 -0
- package/dist/agents/channels/telegram/approval-authorize.js +69 -0
- package/dist/agents/channels/telegram/approval-authorize.js.map +1 -0
- package/dist/agents/channels/telegram/approval-native.d.ts +68 -0
- package/dist/agents/channels/telegram/approval-native.d.ts.map +1 -0
- package/dist/agents/channels/telegram/approval-native.js +94 -0
- package/dist/agents/channels/telegram/approval-native.js.map +1 -0
- package/dist/agents/channels/telegram/command-menu.d.ts +35 -0
- package/dist/agents/channels/telegram/command-menu.d.ts.map +1 -0
- package/dist/agents/channels/telegram/command-menu.js +59 -0
- package/dist/agents/channels/telegram/command-menu.js.map +1 -0
- package/dist/agents/channels/telegram/connection.d.ts +359 -0
- package/dist/agents/channels/telegram/connection.d.ts.map +1 -0
- package/dist/agents/channels/telegram/connection.js +865 -0
- package/dist/agents/channels/telegram/connection.js.map +1 -0
- package/dist/agents/channels/telegram/format.d.ts +48 -0
- package/dist/agents/channels/telegram/format.d.ts.map +1 -0
- package/dist/agents/channels/telegram/format.js +256 -0
- package/dist/agents/channels/telegram/format.js.map +1 -0
- package/dist/agents/channels/telegram/inbound-extras.d.ts +73 -0
- package/dist/agents/channels/telegram/inbound-extras.d.ts.map +1 -0
- package/dist/agents/channels/telegram/inbound-extras.js +231 -0
- package/dist/agents/channels/telegram/inbound-extras.js.map +1 -0
- package/dist/agents/channels/telegram/index.d.ts +14 -0
- package/dist/agents/channels/telegram/index.d.ts.map +1 -0
- package/dist/agents/channels/telegram/index.js +14 -0
- package/dist/agents/channels/telegram/index.js.map +1 -0
- package/dist/agents/channels/telegram/media.d.ts +68 -0
- package/dist/agents/channels/telegram/media.d.ts.map +1 -0
- package/dist/agents/channels/telegram/media.js +143 -0
- package/dist/agents/channels/telegram/media.js.map +1 -0
- package/dist/agents/channels/telegram/module.d.ts +15 -0
- package/dist/agents/channels/telegram/module.d.ts.map +1 -0
- package/dist/agents/channels/telegram/module.js +36 -0
- package/dist/agents/channels/telegram/module.js.map +1 -0
- package/dist/agents/channels/telegram/plugin.d.ts +76 -0
- package/dist/agents/channels/telegram/plugin.d.ts.map +1 -0
- package/dist/agents/channels/telegram/plugin.js +314 -0
- package/dist/agents/channels/telegram/plugin.js.map +1 -0
- package/dist/agents/channels/telegram/probe.d.ts +54 -0
- package/dist/agents/channels/telegram/probe.d.ts.map +1 -0
- package/dist/agents/channels/telegram/probe.js +95 -0
- package/dist/agents/channels/telegram/probe.js.map +1 -0
- package/dist/agents/channels/telegram/webhook.d.ts +55 -0
- package/dist/agents/channels/telegram/webhook.d.ts.map +1 -0
- package/dist/agents/channels/telegram/webhook.js +141 -0
- package/dist/agents/channels/telegram/webhook.js.map +1 -0
- package/dist/agents/extensions/modules/index.d.ts.map +1 -1
- package/dist/agents/extensions/modules/index.js +4 -0
- package/dist/agents/extensions/modules/index.js.map +1 -1
- package/dist/agents/extensions/types.d.ts +72 -2
- package/dist/agents/extensions/types.d.ts.map +1 -1
- package/dist/agents/extensions/types.js.map +1 -1
- package/dist/agents/tools/connect-channel-tool.d.ts +86 -0
- package/dist/agents/tools/connect-channel-tool.d.ts.map +1 -0
- package/dist/agents/tools/connect-channel-tool.js +398 -0
- package/dist/agents/tools/connect-channel-tool.js.map +1 -0
- package/dist/agents/tools/message-action-tool.d.ts +67 -0
- package/dist/agents/tools/message-action-tool.d.ts.map +1 -0
- package/dist/agents/tools/message-action-tool.js +216 -0
- package/dist/agents/tools/message-action-tool.js.map +1 -0
- package/dist/agents/tools/registry.d.ts.map +1 -1
- package/dist/agents/tools/registry.js +19 -0
- package/dist/agents/tools/registry.js.map +1 -1
- package/dist/buildstamp.json +1 -1
- package/dist/cli/commands/channels.d.ts.map +1 -1
- package/dist/cli/commands/channels.js +27 -2
- package/dist/cli/commands/channels.js.map +1 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +77 -27
- package/dist/core/server.js.map +1 -1
- package/dist/cron/service/state.d.ts +10 -0
- package/dist/cron/service/state.d.ts.map +1 -1
- package/dist/cron/service/state.js.map +1 -1
- package/dist/cron/service/timer.d.ts.map +1 -1
- package/dist/cron/service/timer.js +43 -14
- package/dist/cron/service/timer.js.map +1 -1
- package/dist/cron/session-reaper.d.ts +27 -0
- package/dist/cron/session-reaper.d.ts.map +1 -1
- package/dist/cron/session-reaper.js +81 -0
- package/dist/cron/session-reaper.js.map +1 -1
- package/dist/system-prompt/assembler.d.ts +14 -0
- package/dist/system-prompt/assembler.d.ts.map +1 -1
- package/dist/system-prompt/assembler.js +36 -14
- package/dist/system-prompt/assembler.js.map +1 -1
- package/package.json +22 -6
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `connect_channel` — let the crew connect / disconnect a messaging channel
|
|
3
|
+
* from chat, LIVE, without a gateway restart.
|
|
4
|
+
*
|
|
5
|
+
* This is the marquee of the channel-foundation work: the operator can say
|
|
6
|
+
* "connect Telegram, here's the token from @BotFather" and the agent runs ONE
|
|
7
|
+
* grounded tool call that (1) seals the token as a `${VAR}` secret-ref in config
|
|
8
|
+
* (never the raw value on disk), (2) flips `channels.<channel>.enabled = true`
|
|
9
|
+
* via `mutateConfigAtomic`, and (3) starts the channel adapter live through the
|
|
10
|
+
* channel manager (`getActiveChannelManager().startChannel(id)`). No
|
|
11
|
+
* hand-editing brigade.json, no `brigade gateway restart`.
|
|
12
|
+
*
|
|
13
|
+
* GATING — per-call `senderIsOwner` (the cron-tool doctrine, NOT blanket
|
|
14
|
+
* ownerOnly):
|
|
15
|
+
* - `list` / `status` are READ-ONLY and safe for anyone (peers can see what's
|
|
16
|
+
* connected + each channel's health).
|
|
17
|
+
* - `connect` / `disconnect` MOVE A SECURITY BOUNDARY (they wire up / tear
|
|
18
|
+
* down a live messaging surface and persist credentials). A channel peer
|
|
19
|
+
* must NEVER do this, so those actions are refused unless the turn is
|
|
20
|
+
* owner-routed. `senderIsOwner` defaults to TRUE so the TUI / direct-RPC /
|
|
21
|
+
* test paths keep full access; the gateway threads `false` for
|
|
22
|
+
* approved-non-owner channel peers.
|
|
23
|
+
*
|
|
24
|
+
* SECRET HANDLING — durable seal + live env ref (two layers):
|
|
25
|
+
* - DURABLE: the token is written to Brigade's encrypted credential store via
|
|
26
|
+
* `sealChannelToken` (atomic 0600 JSON on disk; AES-256-GCM sealed in convex
|
|
27
|
+
* mode). This is the source of truth ACROSS a gateway reboot — the channel
|
|
28
|
+
* reads it back at start even after the process (and its env) is gone. This
|
|
29
|
+
* closes the gap where an env-only token evaporated on restart and the
|
|
30
|
+
* channel silently failed to authenticate.
|
|
31
|
+
* - LIVE: the raw token is also set into `process.env[BRIGADE_<CHANNEL>_TOKEN]`
|
|
32
|
+
* and the config stores ONLY the literal ref `"${BRIGADE_<CHANNEL>_TOKEN}"`
|
|
33
|
+
* so the just-started adapter resolves it immediately this process.
|
|
34
|
+
* - The resolved token NEVER touches brigade.json (only the `${VAR}` ref), and
|
|
35
|
+
* the tool result masks it.
|
|
36
|
+
*/
|
|
37
|
+
import { Type } from "typebox";
|
|
38
|
+
import { loadConfig } from "../../core/config.js";
|
|
39
|
+
import { mutateConfigAtomic } from "../../config/io.js";
|
|
40
|
+
import { getActiveChannelManager } from "../channels/active-manager.js";
|
|
41
|
+
import { sealChannelToken } from "../channels/channel-secrets.js";
|
|
42
|
+
import { getActiveRegistry } from "../extensions/active-registry.js";
|
|
43
|
+
import { createSubsystemLogger } from "../../logging/subsystem-logger.js";
|
|
44
|
+
import { jsonResult } from "./common.js";
|
|
45
|
+
const log = createSubsystemLogger("tools/connect-channel");
|
|
46
|
+
const ConnectChannelParams = Type.Object({
|
|
47
|
+
action: Type.Union([
|
|
48
|
+
Type.Literal("list"),
|
|
49
|
+
Type.Literal("status"),
|
|
50
|
+
Type.Literal("connect"),
|
|
51
|
+
Type.Literal("disconnect"),
|
|
52
|
+
], {
|
|
53
|
+
description: "list/status: report available + connected channels and their health (read-only, safe). connect: enable a channel + (when needed) store its token + start it LIVE. disconnect: stop + disable a channel at runtime.",
|
|
54
|
+
}),
|
|
55
|
+
channel: Type.Optional(Type.String({
|
|
56
|
+
description: 'Channel id under config.channels (e.g. "telegram", "whatsapp"). Required for connect / disconnect; for status, narrows to one channel.',
|
|
57
|
+
minLength: 1,
|
|
58
|
+
maxLength: 64,
|
|
59
|
+
})),
|
|
60
|
+
token: Type.Optional(Type.String({
|
|
61
|
+
description: "Secret/credential to connect a TOKEN-based channel (e.g. the Telegram bot token from @BotFather). Stored as a `${VAR}` secret-ref — NEVER persisted raw. Omit for channels that pair another way (WhatsApp uses a QR link, not a token — point the operator at `brigade channels link --channel whatsapp`).",
|
|
62
|
+
maxLength: 8192,
|
|
63
|
+
})),
|
|
64
|
+
});
|
|
65
|
+
export function makeConnectChannelTool(opts = {}) {
|
|
66
|
+
// Default to owner-access when omitted so legacy paths (TUI / tests) keep
|
|
67
|
+
// the previous full-access behaviour (same convention as cron-tool).
|
|
68
|
+
const senderIsOwner = opts.senderIsOwner !== false;
|
|
69
|
+
return {
|
|
70
|
+
name: "connect_channel",
|
|
71
|
+
label: "Connect Channel",
|
|
72
|
+
displaySummary: "connecting a messaging channel",
|
|
73
|
+
// NOTE: deliberately NOT `ownerOnly: true`. list/status are safe for
|
|
74
|
+
// peers; the per-call gate below refuses ONLY the mutating actions.
|
|
75
|
+
description: [
|
|
76
|
+
"Connect or disconnect a messaging channel (Telegram, WhatsApp, …) from chat, live, without a gateway restart.",
|
|
77
|
+
"list / status: report available + connected channels and their health (read-only — anyone can call).",
|
|
78
|
+
"connect {channel, token?}: OWNER-ONLY. Enables the channel, seals a provided token as a `${VAR}` secret-ref (never stored raw), and starts it LIVE via the channel manager. For token channels (Telegram) pass `token`; for QR channels (WhatsApp) omit it and tell the operator to run `brigade channels link --channel whatsapp`.",
|
|
79
|
+
"disconnect {channel}: OWNER-ONLY. Stops + disables the channel at runtime.",
|
|
80
|
+
"Call connect/disconnect ONLY on explicit operator request; report exactly what changed. Never hand-edit brigade.json — the config-write guard refuses it.",
|
|
81
|
+
].join(" "),
|
|
82
|
+
parameters: ConnectChannelParams,
|
|
83
|
+
execute: async (_toolCallId, args) => {
|
|
84
|
+
const action = args.action;
|
|
85
|
+
// READ-ONLY actions — safe for anyone.
|
|
86
|
+
if (action === "list" || action === "status") {
|
|
87
|
+
const wantChannel = (args.channel ?? "").trim().toLowerCase();
|
|
88
|
+
const views = buildChannelViews(wantChannel || undefined);
|
|
89
|
+
if (action === "status" && wantChannel) {
|
|
90
|
+
const view = views.find((v) => v.channel === wantChannel);
|
|
91
|
+
if (!view) {
|
|
92
|
+
return jsonResult({
|
|
93
|
+
action,
|
|
94
|
+
ok: false,
|
|
95
|
+
message: `No channel adapter registered with id "${wantChannel}". Available: ${listAvailableIds().join(", ") || "none"}.`,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return jsonResult({
|
|
99
|
+
action,
|
|
100
|
+
ok: true,
|
|
101
|
+
channelState: view,
|
|
102
|
+
message: describeView(view),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return jsonResult({
|
|
106
|
+
action,
|
|
107
|
+
ok: true,
|
|
108
|
+
channels: views,
|
|
109
|
+
message: summarizeViews(views),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// MUTATING actions — owner-gated (per-call).
|
|
113
|
+
if (!senderIsOwner) {
|
|
114
|
+
return jsonResult({
|
|
115
|
+
action,
|
|
116
|
+
ok: false,
|
|
117
|
+
message: "connect_channel: connecting or disconnecting a channel is owner-only — a channel peer cannot alter the operator's channels. Ask the operator to do this from the TUI.",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const channel = (args.channel ?? "").trim().toLowerCase();
|
|
121
|
+
if (!channel) {
|
|
122
|
+
return jsonResult({
|
|
123
|
+
action,
|
|
124
|
+
ok: false,
|
|
125
|
+
message: `connect_channel ${action}: a channel id is required (e.g. "telegram").`,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// The adapter must be a registered, bundled channel — refuse arbitrary ids.
|
|
129
|
+
const known = listAvailableIds();
|
|
130
|
+
if (!known.includes(channel)) {
|
|
131
|
+
return jsonResult({
|
|
132
|
+
action,
|
|
133
|
+
ok: false,
|
|
134
|
+
message: `connect_channel ${action}: no channel adapter registered with id "${channel}". Available: ${known.join(", ") || "none"}.`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (action === "connect")
|
|
138
|
+
return connectChannel(channel, args.token);
|
|
139
|
+
return disconnectChannel(channel);
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/* ───────────────────────────── connect ───────────────────────────── */
|
|
144
|
+
async function connectChannel(channel, rawToken) {
|
|
145
|
+
const token = (rawToken ?? "").trim();
|
|
146
|
+
// 1) Persist: enable the channel + (when a token was supplied) seal it.
|
|
147
|
+
// Two-layer seal so the token survives BOTH this process and a reboot:
|
|
148
|
+
// (a) DURABLE: write the token to the encrypted credential store
|
|
149
|
+
// (`sealChannelToken`) — atomic 0600 on disk, AES-256-GCM in convex
|
|
150
|
+
// mode. This is what the channel reads back at start AFTER a gateway
|
|
151
|
+
// restart (env is gone by then). Source of truth across reboots.
|
|
152
|
+
// (b) LIVE: set `process.env[VAR]` + a `${VAR}` ref in brigade.json so
|
|
153
|
+
// the just-started adapter resolves the token immediately this
|
|
154
|
+
// process without re-reading the sealed store.
|
|
155
|
+
// The raw token NEVER lands in brigade.json (only the `${VAR}` ref) and
|
|
156
|
+
// the tool result masks it.
|
|
157
|
+
let envVarName;
|
|
158
|
+
if (token) {
|
|
159
|
+
// (a) durable encrypted seal — survives the reboot.
|
|
160
|
+
try {
|
|
161
|
+
sealChannelToken(channel, token);
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
log.warn("connect_channel: durable token seal failed (continuing with env ref)", {
|
|
165
|
+
channel,
|
|
166
|
+
error: err instanceof Error ? err.message : String(err),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// (b) live env for immediate resolution this process.
|
|
170
|
+
envVarName = secretEnvVarName(channel);
|
|
171
|
+
process.env[envVarName] = token;
|
|
172
|
+
}
|
|
173
|
+
const next = await mutateConfigAtomic((current) => {
|
|
174
|
+
const merged = { ...current };
|
|
175
|
+
const channels = {
|
|
176
|
+
...(merged.channels ?? {}),
|
|
177
|
+
};
|
|
178
|
+
const entry = { ...(channels[channel] ?? {}) };
|
|
179
|
+
entry.enabled = true;
|
|
180
|
+
if (envVarName) {
|
|
181
|
+
// Store the literal ref string — a brand-new field is written verbatim
|
|
182
|
+
// by restoreEnvVarRefsRecursive, so `${VAR}` lands on disk (NOT the
|
|
183
|
+
// resolved token). For Telegram this is the canonical `botToken` slot.
|
|
184
|
+
entry[tokenConfigKey(channel)] = `\${${envVarName}}`;
|
|
185
|
+
}
|
|
186
|
+
channels[channel] = entry;
|
|
187
|
+
merged.channels = channels;
|
|
188
|
+
return merged;
|
|
189
|
+
});
|
|
190
|
+
// 2) Start it LIVE through the channel manager (no gateway restart). Pass the
|
|
191
|
+
// freshly-written config so isConfigured + start() see enabled + the token.
|
|
192
|
+
const manager = getActiveChannelManager();
|
|
193
|
+
if (!manager) {
|
|
194
|
+
// Honest fallback — config is written, but no live manager to start it.
|
|
195
|
+
log.info("connect_channel: config written but no live channel manager", { channel });
|
|
196
|
+
return jsonResult({
|
|
197
|
+
action: "connect",
|
|
198
|
+
ok: true,
|
|
199
|
+
started: false,
|
|
200
|
+
channelState: viewFor(channel, next),
|
|
201
|
+
message: `Configured "${channel}" (enabled${token ? " + token sealed" : ""}). The gateway isn't running, so it will connect on next start. Run \`brigade gateway\` (or restart it) to connect now.`,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const result = await manager.startChannel(channel, next);
|
|
205
|
+
const view = viewFor(channel, next);
|
|
206
|
+
if (result.ok) {
|
|
207
|
+
return jsonResult({
|
|
208
|
+
action: "connect",
|
|
209
|
+
ok: true,
|
|
210
|
+
started: result.started,
|
|
211
|
+
channelState: view,
|
|
212
|
+
message: result.started
|
|
213
|
+
? `Connected "${channel}" — it's live now${token ? " (token sealed as a secret ref)" : ""}. ${describeView(view)}`
|
|
214
|
+
: `"${channel}" is already connected. ${describeView(view)}`,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
// Config is written but the live start didn't take (e.g. still not
|
|
218
|
+
// configured, or the adapter's start threw). Be HONEST: report it and give
|
|
219
|
+
// the restart fallback.
|
|
220
|
+
return jsonResult({
|
|
221
|
+
action: "connect",
|
|
222
|
+
ok: false,
|
|
223
|
+
started: false,
|
|
224
|
+
channelState: view,
|
|
225
|
+
message: `Saved config for "${channel}" but couldn't start it live: ${result.message ?? result.reason ?? "unknown error"}. ${remediationFor(channel)}`,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
/* ───────────────────────────── disconnect ───────────────────────────── */
|
|
229
|
+
async function disconnectChannel(channel) {
|
|
230
|
+
// 1) Stop it live (if a manager is running + it's started).
|
|
231
|
+
const manager = getActiveChannelManager();
|
|
232
|
+
let stopped = false;
|
|
233
|
+
if (manager) {
|
|
234
|
+
const res = await manager.stopChannel(channel);
|
|
235
|
+
stopped = res.stopped;
|
|
236
|
+
}
|
|
237
|
+
// 2) Disable it in config so it doesn't come back on the next boot.
|
|
238
|
+
const next = await mutateConfigAtomic((current) => {
|
|
239
|
+
const merged = { ...current };
|
|
240
|
+
const channels = {
|
|
241
|
+
...(merged.channels ?? {}),
|
|
242
|
+
};
|
|
243
|
+
const entry = { ...(channels[channel] ?? {}) };
|
|
244
|
+
entry.enabled = false;
|
|
245
|
+
channels[channel] = entry;
|
|
246
|
+
merged.channels = channels;
|
|
247
|
+
return merged;
|
|
248
|
+
});
|
|
249
|
+
const view = viewFor(channel, next);
|
|
250
|
+
return jsonResult({
|
|
251
|
+
action: "disconnect",
|
|
252
|
+
ok: true,
|
|
253
|
+
stopped,
|
|
254
|
+
channelState: view,
|
|
255
|
+
message: stopped
|
|
256
|
+
? `Disconnected "${channel}" — stopped it live and disabled it in config (won't reconnect on restart).`
|
|
257
|
+
: `Disabled "${channel}" in config. It wasn't running, so nothing to stop.`,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
/* ───────────────────────────── helpers ───────────────────────────── */
|
|
261
|
+
/**
|
|
262
|
+
* Env var that backs a channel's sealed token. `telegram` →
|
|
263
|
+
* `BRIGADE_TELEGRAM_TOKEN`. Uppercased + non-alnum→`_` so any channel id
|
|
264
|
+
* yields a legal env name (and matches the `${VAR}` SECRET_REF_PATTERN, which
|
|
265
|
+
* requires `[A-Z_][A-Z0-9_]*`).
|
|
266
|
+
*/
|
|
267
|
+
function secretEnvVarName(channel) {
|
|
268
|
+
const slug = channel.toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
269
|
+
return `BRIGADE_${slug || "CHANNEL"}_TOKEN`;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Which config key under `channels.<id>` holds the token. Telegram's adapter
|
|
273
|
+
* reads `botToken`; default to that name for unknown token channels too (it's
|
|
274
|
+
* the established convention).
|
|
275
|
+
*/
|
|
276
|
+
function tokenConfigKey(_channel) {
|
|
277
|
+
return "botToken";
|
|
278
|
+
}
|
|
279
|
+
/** All registered channel ids (bundled + user). Empty when no registry mounted. */
|
|
280
|
+
function listAvailableIds() {
|
|
281
|
+
const registry = getActiveRegistry();
|
|
282
|
+
if (!registry)
|
|
283
|
+
return [];
|
|
284
|
+
return registry.channels.map((a) => a.id);
|
|
285
|
+
}
|
|
286
|
+
/** Set of channel ids currently started in this gateway process. */
|
|
287
|
+
function connectedIdSet() {
|
|
288
|
+
const manager = getActiveChannelManager();
|
|
289
|
+
return new Set(manager ? manager.started : []);
|
|
290
|
+
}
|
|
291
|
+
/** Build a view for every registered channel (or just one when `only` is set). */
|
|
292
|
+
function buildChannelViews(only) {
|
|
293
|
+
const registry = getActiveRegistry();
|
|
294
|
+
if (!registry)
|
|
295
|
+
return [];
|
|
296
|
+
const cfg = loadConfig();
|
|
297
|
+
const connected = connectedIdSet();
|
|
298
|
+
const manager = getActiveChannelManager();
|
|
299
|
+
const out = [];
|
|
300
|
+
for (const adapter of registry.channels) {
|
|
301
|
+
if (only && adapter.id !== only)
|
|
302
|
+
continue;
|
|
303
|
+
const enabled = cfg.channels?.[adapter.id]?.enabled === true;
|
|
304
|
+
let configured = false;
|
|
305
|
+
try {
|
|
306
|
+
configured = adapter.isConfigured(cfg);
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
/* adapters shouldn't throw here, but be safe */
|
|
310
|
+
}
|
|
311
|
+
const isConnected = connected.has(adapter.id);
|
|
312
|
+
const view = {
|
|
313
|
+
channel: adapter.id,
|
|
314
|
+
label: adapter.label,
|
|
315
|
+
enabled,
|
|
316
|
+
configured,
|
|
317
|
+
connected: isConnected,
|
|
318
|
+
};
|
|
319
|
+
// Health only meaningful for a started adapter exposing health().
|
|
320
|
+
if (isConnected && manager) {
|
|
321
|
+
const live = manager.adapter(adapter.id);
|
|
322
|
+
if (live && typeof live.health === "function") {
|
|
323
|
+
const h = live.health();
|
|
324
|
+
view.health = {
|
|
325
|
+
ok: h.ok,
|
|
326
|
+
...(!h.ok && h.reason ? { reason: h.reason } : {}),
|
|
327
|
+
...(!h.ok && h.remediation ? { remediation: h.remediation } : {}),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
out.push(view);
|
|
332
|
+
}
|
|
333
|
+
return out;
|
|
334
|
+
}
|
|
335
|
+
/** Single-channel view from a specific config snapshot (post-write). */
|
|
336
|
+
function viewFor(channel, cfg) {
|
|
337
|
+
const registry = getActiveRegistry();
|
|
338
|
+
const adapter = registry?.channels.find((a) => a.id === channel);
|
|
339
|
+
const enabled = cfg.channels?.[channel]?.enabled === true;
|
|
340
|
+
const connected = connectedIdSet().has(channel);
|
|
341
|
+
let configured = false;
|
|
342
|
+
if (adapter) {
|
|
343
|
+
try {
|
|
344
|
+
configured = adapter.isConfigured(cfg);
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
/* ignore */
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const view = {
|
|
351
|
+
channel,
|
|
352
|
+
label: adapter?.label ?? channel,
|
|
353
|
+
enabled,
|
|
354
|
+
configured,
|
|
355
|
+
connected,
|
|
356
|
+
};
|
|
357
|
+
const manager = getActiveChannelManager();
|
|
358
|
+
if (connected && manager) {
|
|
359
|
+
const live = manager.adapter(channel);
|
|
360
|
+
if (live && typeof live.health === "function") {
|
|
361
|
+
const h = live.health();
|
|
362
|
+
view.health = {
|
|
363
|
+
ok: h.ok,
|
|
364
|
+
...(!h.ok && h.reason ? { reason: h.reason } : {}),
|
|
365
|
+
...(!h.ok && h.remediation ? { remediation: h.remediation } : {}),
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return view;
|
|
370
|
+
}
|
|
371
|
+
function describeView(v) {
|
|
372
|
+
const state = v.connected
|
|
373
|
+
? v.health && !v.health.ok
|
|
374
|
+
? `connected but degraded (${v.health.reason ?? "unknown"})`
|
|
375
|
+
: "connected"
|
|
376
|
+
: v.enabled
|
|
377
|
+
? "enabled but not connected"
|
|
378
|
+
: "not connected";
|
|
379
|
+
return `${v.label}: ${state}.`;
|
|
380
|
+
}
|
|
381
|
+
function summarizeViews(views) {
|
|
382
|
+
if (views.length === 0)
|
|
383
|
+
return "No channels are registered.";
|
|
384
|
+
const connected = views.filter((v) => v.connected).map((v) => v.label);
|
|
385
|
+
const available = views.filter((v) => !v.connected).map((v) => v.label);
|
|
386
|
+
const parts = [];
|
|
387
|
+
parts.push(connected.length > 0 ? `Connected: ${connected.join(", ")}.` : "No channels connected.");
|
|
388
|
+
if (available.length > 0)
|
|
389
|
+
parts.push(`Available to connect: ${available.join(", ")}.`);
|
|
390
|
+
return parts.join(" ");
|
|
391
|
+
}
|
|
392
|
+
function remediationFor(channel) {
|
|
393
|
+
if (channel === "whatsapp") {
|
|
394
|
+
return "WhatsApp links via QR, not a token — run `brigade channels link --channel whatsapp` (gateway stopped), then it connects on next start.";
|
|
395
|
+
}
|
|
396
|
+
return `Check the token/settings, then retry — or restart the gateway with \`brigade gateway\` to pick up the saved config.`;
|
|
397
|
+
}
|
|
398
|
+
//# sourceMappingURL=connect-channel-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect-channel-tool.js","sourceRoot":"","sources":["../../../src/agents/tools/connect-channel-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `message_action` — agent-callable edit / delete / react on a channel message.
|
|
3
|
+
*
|
|
4
|
+
* Companion to `send_message` (which creates messages) and `connect_channel`
|
|
5
|
+
* (which wires up channels). Where `send_message` sends NEW text, this tool acts
|
|
6
|
+
* on an EXISTING message the agent already sent or received: edit its text,
|
|
7
|
+
* delete it, react to it with an emoji, or pin/unpin it. It is the one central
|
|
8
|
+
* surface for every channel's message-action capability — a channel opts in by
|
|
9
|
+
* implementing `ChannelAdapter.handleAction` + advertising the matching
|
|
10
|
+
* `capabilities` flag; this tool dispatches through the active channel manager
|
|
11
|
+
* and PRE-CHECKS that flag so an unsupported action fails cleanly without ever
|
|
12
|
+
* touching the adapter.
|
|
13
|
+
*
|
|
14
|
+
* Ownership: BLANKET owner-only. Editing or deleting an arbitrary message is a
|
|
15
|
+
* privileged act — a channel-routed peer must NEVER reach into the operator's
|
|
16
|
+
* conversations and mutate or remove messages. Unlike `send_message` (which a
|
|
17
|
+
* peer may use to reply to their OWN chat), there is no safe peer subset here,
|
|
18
|
+
* so the whole tool is refused for non-owner turns via a per-call gate (kept as
|
|
19
|
+
* a per-call gate, not a registration-time `ownerOnly`, so the refusal carries
|
|
20
|
+
* a clear actionable message instead of the tool silently vanishing).
|
|
21
|
+
*
|
|
22
|
+
* Text safety: `edit` text is run through the central `sanitizeReplyForChannel`
|
|
23
|
+
* (the same reasoning-leak scrubber every outbound reply uses) before it reaches
|
|
24
|
+
* the adapter, so an edited message can't leak `<think>` content either.
|
|
25
|
+
*/
|
|
26
|
+
import { Type } from "typebox";
|
|
27
|
+
import type { ChannelApprovalRoute } from "../channels/approval-router.js";
|
|
28
|
+
import type { BrigadeTool } from "./types.js";
|
|
29
|
+
declare const MessageActionParams: Type.TObject<{
|
|
30
|
+
channel: Type.TString;
|
|
31
|
+
to: Type.TString;
|
|
32
|
+
action: Type.TObject<{
|
|
33
|
+
kind: Type.TUnion<[Type.TLiteral<"edit">, Type.TLiteral<"delete">, Type.TLiteral<"react">, Type.TLiteral<"pin">, Type.TLiteral<"unpin">]>;
|
|
34
|
+
messageId: Type.TOptional<Type.TString>;
|
|
35
|
+
text: Type.TOptional<Type.TString>;
|
|
36
|
+
emoji: Type.TOptional<Type.TString>;
|
|
37
|
+
}>;
|
|
38
|
+
accountId: Type.TOptional<Type.TString>;
|
|
39
|
+
}>;
|
|
40
|
+
interface MessageActionDetails {
|
|
41
|
+
channel: string;
|
|
42
|
+
to: string;
|
|
43
|
+
kind: string;
|
|
44
|
+
ok: boolean;
|
|
45
|
+
messageId?: string;
|
|
46
|
+
error?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface MakeMessageActionToolOptions {
|
|
49
|
+
/** Active channel context for this turn (unused for routing today; kept for parity + future auto-fill). */
|
|
50
|
+
channelContext?: ChannelApprovalRoute;
|
|
51
|
+
/**
|
|
52
|
+
* Whether the calling turn is the workspace owner. Defaults to `true` so
|
|
53
|
+
* legacy paths (TUI / tests) keep access. A non-owner turn is refused
|
|
54
|
+
* wholesale — there is no safe peer subset for message mutation.
|
|
55
|
+
*/
|
|
56
|
+
senderIsOwner?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* The calling agent's id — used to resolve "my last message" when the
|
|
59
|
+
* action omits `messageId` (the pipeline records the agent's last sent id
|
|
60
|
+
* per conversation). When omitted, the last-sent fallback is unavailable
|
|
61
|
+
* and an action with no `messageId` is refused as before.
|
|
62
|
+
*/
|
|
63
|
+
agentId?: string;
|
|
64
|
+
}
|
|
65
|
+
export declare function makeMessageActionTool(opts?: MakeMessageActionToolOptions): BrigadeTool<typeof MessageActionParams, MessageActionDetails>;
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=message-action-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-action-tool.d.ts","sourceRoot":"","sources":["../../../src/agents/tools/message-action-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAK/B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAU3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAkB9C,QAAA,MAAM,mBAAmB;;;;;;;;;;EAqCvB,CAAC;AAEH,UAAU,oBAAoB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,4BAA4B;IAC5C,2GAA2G;IAC3G,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAiCD,wBAAgB,qBAAqB,CACpC,IAAI,GAAE,4BAAiC,GACrC,WAAW,CAAC,OAAO,mBAAmB,EAAE,oBAAoB,CAAC,CA+I/D"}
|