@spinabot/brigade 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +16 -3
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ════════════════════════════════════════════════════════════════════════════
|
|
3
|
+
* Brigade Channel SDK — the COMPLETE surface for authoring a channel
|
|
4
|
+
* ════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
*
|
|
6
|
+
* One import for everything a new channel adapter (Slack, Discord, iMessage, …)
|
|
7
|
+
* needs: the CONTRACT it implements (BOTH the single-account `ChannelAdapter`
|
|
8
|
+
* AND the multi-account `ChannelPlugin`), the SHARED HELPERS every channel
|
|
9
|
+
* reuses (the inbound pipeline, the approval router, the last-channel pin), and
|
|
10
|
+
* the central capabilities (inline-button approvals, message actions, the
|
|
11
|
+
* durable token seal, the webhook HTTP route). The goal is that the NEXT channel
|
|
12
|
+
* — including a multi-account + webhook + native-commands channel like Discord —
|
|
13
|
+
* is built ENTIRELY on this barrel: no reaching across into `whatsapp/`,
|
|
14
|
+
* `../types.*`, `../inbound-pipeline`, `../approval-router`, `../manager`, or
|
|
15
|
+
* `../../extensions/` for a stray type or helper.
|
|
16
|
+
*
|
|
17
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
18
|
+
* TWO AUTHORING PATHS
|
|
19
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
20
|
+
*
|
|
21
|
+
* A. SINGLE-ACCOUNT adapter (`ChannelAdapter`) — one live connection per
|
|
22
|
+
* gateway, started by the legacy `startChannels` manager. Simplest path;
|
|
23
|
+
* implement `start/stop/sendText/…` and register via `b.channel(adapter)`.
|
|
24
|
+
*
|
|
25
|
+
* B. MULTI-ACCOUNT plugin (`ChannelPlugin`) — run N accounts of the same
|
|
26
|
+
* channel at once (`channels.<id>.accounts: [{ id, … }, …]`). You implement
|
|
27
|
+
* the sub-adapters (`config`/`gateway`/`outbound`/`security`/`status`/
|
|
28
|
+
* `actions`/`secrets`/`approvalCapability`), partition per-account runtime
|
|
29
|
+
* state in a `Map<accountId, …>`, and drive each account's inbound through
|
|
30
|
+
* the SHARED `runChannelInboundPipeline` so every account carries the
|
|
31
|
+
* identical ACL + debounce + abort + approval surface. Telegram's
|
|
32
|
+
* `plugin.ts` is the reference; mirror it.
|
|
33
|
+
*
|
|
34
|
+
* Most real channels ship BOTH: the `ChannelAdapter` is the per-connection
|
|
35
|
+
* worker, and the `ChannelPlugin` wraps `createXAdapter()` once per account.
|
|
36
|
+
*
|
|
37
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
38
|
+
* HOW TO AUTHOR A CHANNEL — the 8-file skeleton
|
|
39
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
40
|
+
*
|
|
41
|
+
* Mirror the `telegram/` layout (the reference token-based channel):
|
|
42
|
+
*
|
|
43
|
+
* channels/<id>/
|
|
44
|
+
* ├─ module.ts register: `defineModule({ id, register(b){ b.channel(createXAdapter()) } })`
|
|
45
|
+
* ├─ adapter.ts implement `ChannelAdapter` (id/label/start/stop/sendText/…)
|
|
46
|
+
* ├─ plugin.ts implement `ChannelPlugin` (multi-account: config/gateway/outbound/…)
|
|
47
|
+
* ├─ connection.ts the live transport (lazy-import the heavy SDK here)
|
|
48
|
+
* ├─ account-config.ts resolve per-account config + token (see secret seal below)
|
|
49
|
+
* ├─ format.ts markdown → the channel's native markup
|
|
50
|
+
* ├─ inbound-extras.ts normalize provider payloads → `InboundMessage` fields
|
|
51
|
+
* ├─ command-menu.ts map `buildBundledCommands(adapter)` → native `/`-menu (optional)
|
|
52
|
+
* ├─ webhook.ts build the inbound `HttpRoute` for push transport (optional)
|
|
53
|
+
* ├─ media.ts in/out media (run `validateOutboundMediaPath` on send)
|
|
54
|
+
* └─ index.ts re-export the public surface
|
|
55
|
+
*
|
|
56
|
+
* plus tests alongside each (`*.test.ts`).
|
|
57
|
+
*
|
|
58
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
59
|
+
* REGISTRATION
|
|
60
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
61
|
+
*
|
|
62
|
+
* 1. `module.ts` calls `b.channel(createXAdapter())` inside `defineModule`.
|
|
63
|
+
* 2. Add the module to the bundled module list (`extensions/modules/index.ts`).
|
|
64
|
+
* 3. Account config lives under `~/.brigade/channels/<id>/<accountId>/`.
|
|
65
|
+
* 4. Lazy-import the heavy transport SDK inside `connection.ts` (Baileys /
|
|
66
|
+
* grammY style) so a non-X boot never pays for it.
|
|
67
|
+
* 5. Implement reconnect with `nextBackoffDelay({ attempt, initialMs, maxMs,
|
|
68
|
+
* factor, jitter })` (do NOT hand-roll the curve) + a capped attempt count.
|
|
69
|
+
* 6. Run inbound through dedupe (`createDedupeCache`) and outbound media
|
|
70
|
+
* through `validateOutboundMediaPath` before uploading bytes.
|
|
71
|
+
*
|
|
72
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
73
|
+
* MULTI-ACCOUNT PLUGIN PATH (`ChannelPlugin`)
|
|
74
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
75
|
+
*
|
|
76
|
+
* 1. Declare `id` / `meta` / `capabilities` / `config` (required) plus the
|
|
77
|
+
* sub-adapters you support (`gateway`/`outbound`/`security`/`status`/
|
|
78
|
+
* `actions`/`secrets`/`approvalCapability`). Omit a slot to opt out; the
|
|
79
|
+
* manager pre-checks `capabilities.<flag>` before calling an adapter.
|
|
80
|
+
* 2. `config.listAccountIds(cfg)` + `config.resolveAccount(cfg, id)` drive
|
|
81
|
+
* multi-account discovery. Make the legacy single-account `ChannelAdapter`
|
|
82
|
+
* STEP ASIDE when >1 account is configured (its `isConfigured` returns
|
|
83
|
+
* false for the default account) so the two paths never double-start.
|
|
84
|
+
* 3. In `gateway.startAccount`, build a per-account pipeline with
|
|
85
|
+
* `createInboundPipelineContext({ adapter, config, agentId, runTurn,
|
|
86
|
+
* commandMap, parentAbort })` and feed each inbound through
|
|
87
|
+
* `runChannelInboundPipeline(pipeline, msg)` (stamp `msg.accountId`).
|
|
88
|
+
* Build the command map from `buildBundledCommands(adapter)`.
|
|
89
|
+
* 4. Register a PER-ACCOUNT approval dispatcher with
|
|
90
|
+
* `registerChannelApprovalDispatcher(channelId, accountId, dispatcher)` on
|
|
91
|
+
* start and `removeChannelApprovalDispatcher(channelId, accountId)` on stop,
|
|
92
|
+
* so an exec-gate prompt raised by a turn on (channel, accountId) replies on
|
|
93
|
+
* that same account — not the channel default.
|
|
94
|
+
* 5. Take `runTurn` from the gateway via `StartChannelsArgs["runTurn"]`.
|
|
95
|
+
*
|
|
96
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
97
|
+
* WEBHOOK / PUSH TRANSPORT (`HttpRoute`)
|
|
98
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
99
|
+
*
|
|
100
|
+
* For channels that receive updates via an inbound POST (Telegram webhook
|
|
101
|
+
* mode, Slack Events API, …) instead of long-polling:
|
|
102
|
+
* • Build an `HttpRoute` (the `HttpRoute` type is re-exported here) whose
|
|
103
|
+
* handler verifies the provider's signature / secret header FIRST (before
|
|
104
|
+
* parsing the body), then feeds the parsed update into the started
|
|
105
|
+
* adapter's normalize+dedupe+dispatch path.
|
|
106
|
+
* • Register it from `module.ts` via `b.httpRoute(route)`, gated on config so
|
|
107
|
+
* a default polling install exposes NO inbound HTTP surface.
|
|
108
|
+
* • Set `auth: "none"` ONLY when the provider authenticates via a signed
|
|
109
|
+
* payload the handler verifies itself (the gateway can't present operator-
|
|
110
|
+
* auth to a third-party webhook); otherwise use `auth: "operator"`.
|
|
111
|
+
*
|
|
112
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
113
|
+
* NATIVE COMMAND MENU
|
|
114
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
115
|
+
*
|
|
116
|
+
* Brigade owns the channel command set centrally (`buildBundledCommands(adapter)`
|
|
117
|
+
* → `/help`, `/status`, `/allowlist`, `/agent`, `/agents`, `/whoami`, `/org`,
|
|
118
|
+
* plus module-registered `ChannelCommand`s). A channel with a native `/`-menu
|
|
119
|
+
* (Telegram `setMyCommands`, Discord application commands) maps that set onto
|
|
120
|
+
* the provider's shape on connect — see `telegram/command-menu.ts`. Advertise
|
|
121
|
+
* `capabilities.nativeCommands: true`.
|
|
122
|
+
*
|
|
123
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
124
|
+
* OPTING INTO CENTRAL CAPABILITIES
|
|
125
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
126
|
+
*
|
|
127
|
+
* • Inline-button approvals: set `adapter.approvalCapability.sendApprovalPrompt`
|
|
128
|
+
* (render `buildApprovalCallbackButtons(approvalId)` as native buttons whose
|
|
129
|
+
* `callback_data` is each button's `.data`). Deliver the press back as
|
|
130
|
+
* `InboundMessage.callbackQuery` — the central pipeline decodes + resolves
|
|
131
|
+
* it. Add `authorizeApprover` to refuse non-operator pressers.
|
|
132
|
+
* • Message actions (edit / delete / react / pin): implement
|
|
133
|
+
* `adapter.handleAction(...)` and advertise the matching `capabilities`
|
|
134
|
+
* flags (`edit` / `unsend` / `reactions` / `reply`). The central
|
|
135
|
+
* `message_action` tool pre-checks the flag before calling you.
|
|
136
|
+
* • Outbound id: return `{ messageId }` from `sendText`/`sendMedia` so the
|
|
137
|
+
* agent can reference "my last message" via `message_action`.
|
|
138
|
+
* • Durable token: `connect_channel` seals tokens via `sealChannelToken`;
|
|
139
|
+
* read yours back at start with `readSealedChannelToken("<id>")` as a token
|
|
140
|
+
* source (survives a gateway reboot when the live env is gone).
|
|
141
|
+
*
|
|
142
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
143
|
+
* ENUMERATION TESTS TO UPDATE when you add a channel-related agent tool
|
|
144
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
145
|
+
*
|
|
146
|
+
* • `agents/tools/registry.test.ts` — the sorted tool-name list + count
|
|
147
|
+
* • `agents/session-wiring.test.ts` — the brigade-tool list + counts
|
|
148
|
+
* • `agents/tools/owner-only.test.ts` — the customTools count + name list
|
|
149
|
+
*
|
|
150
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
151
|
+
* DESIGN NOTE — RE-EXPORT, never RELOCATE
|
|
152
|
+
* ──────────────────────────────────────────────────────────────────────────
|
|
153
|
+
*
|
|
154
|
+
* Helpers like `chunkText` physically live where they were first written
|
|
155
|
+
* (`whatsapp/chunk.js`) and WhatsApp imports them directly. Moving a file would
|
|
156
|
+
* change WhatsApp's import and risk the one channel that works perfectly, so
|
|
157
|
+
* this barrel RE-EXPORTS from where things already are — it has zero runtime
|
|
158
|
+
* behaviour of its own. The codec + the durable-seal + the backoff helper DO
|
|
159
|
+
* live in `channels/` (they are channel-neutral by design) and are re-exported
|
|
160
|
+
* here as the canonical author-facing surface.
|
|
161
|
+
*/
|
|
162
|
+
/* ───────────────────────── contract: register ───────────────────────── */
|
|
163
|
+
export {
|
|
164
|
+
/** Register a channel through the extension seam: `defineModule({ id, register(b){ b.channel(adapter) } })`. */
|
|
165
|
+
defineModule, } from "../extensions/types.js";
|
|
166
|
+
/* ───────────────────────── shared helper: outbound chunking ───────────────────────── */
|
|
167
|
+
export {
|
|
168
|
+
/**
|
|
169
|
+
* Split a long outbound message into provider-safe chunks WITHOUT shredding
|
|
170
|
+
* code fences / paragraphs. Lives under `whatsapp/` for history; it is
|
|
171
|
+
* channel-agnostic (Telegram reuses it with `{ limit: 4096 }`). Re-exported
|
|
172
|
+
* here so new channels don't reach across into the WhatsApp folder.
|
|
173
|
+
*/
|
|
174
|
+
chunkText, } from "./whatsapp/chunk.js";
|
|
175
|
+
/* ───────────────────────── shared helper: inbound dedupe ───────────────────────── */
|
|
176
|
+
export {
|
|
177
|
+
/** Build a per-channel inbound dedupe cache (claim-once, LRU + TTL). */
|
|
178
|
+
createDedupeCache, } from "./dedupe.js";
|
|
179
|
+
/* ───────────────────────── shared helper: outbound media guard ───────────────────────── */
|
|
180
|
+
export {
|
|
181
|
+
/**
|
|
182
|
+
* Refuse to ATTACH a local secret / system file on outbound media — the
|
|
183
|
+
* content-exfil guard every `sendMedia` path must run before uploading
|
|
184
|
+
* a local path's bytes to a conversation.
|
|
185
|
+
*/
|
|
186
|
+
validateOutboundMediaPath, } from "../../security/media-path-guard.js";
|
|
187
|
+
/* ───────────────────────── shared helper: structured logger ───────────────────────── */
|
|
188
|
+
export {
|
|
189
|
+
/** Named JSON subsystem logger — `createSubsystemLogger("channels/<id>")`. */
|
|
190
|
+
createSubsystemLogger, } from "../../logging/subsystem-logger.js";
|
|
191
|
+
/* ───────────────────────── shared helper: reconnect backoff ───────────────────────── */
|
|
192
|
+
export {
|
|
193
|
+
/**
|
|
194
|
+
* Jittered exponential reconnect-delay helper shared by every channel.
|
|
195
|
+
* Pass the schedule ({ attempt, initialMs, maxMs, factor, jitter }); the
|
|
196
|
+
* arithmetic is WhatsApp's proven curve. WhatsApp + Telegram both delegate
|
|
197
|
+
* to it; a new channel should too instead of hand-rolling its own.
|
|
198
|
+
*/
|
|
199
|
+
nextBackoffDelay, } from "./backoff.js";
|
|
200
|
+
/* ───────────────────────── central: inline-button approval codec ───────────────────────── */
|
|
201
|
+
export {
|
|
202
|
+
/** Encode `{ approvalId, decision }` into a <=64-byte callback payload (undefined if oversized). */
|
|
203
|
+
encodeApprovalCallback,
|
|
204
|
+
/** Decode a callback payload back to `{ approvalId, decision }` (null if not ours / malformed). */
|
|
205
|
+
decodeApprovalCallback,
|
|
206
|
+
/** True iff a string fits the universal 64-byte callback-data budget. */
|
|
207
|
+
fitsApprovalCallback,
|
|
208
|
+
/** Build the standard Allow once / Allow always / Deny buttons (label + encoded payload). */
|
|
209
|
+
buildApprovalCallbackButtons,
|
|
210
|
+
/** The universal callback-data byte ceiling (Telegram's 64). */
|
|
211
|
+
APPROVAL_CALLBACK_MAX_BYTES, } from "./approval-callback-codec.js";
|
|
212
|
+
/* ───────────────────────── central: durable channel-token seal ───────────────────────── */
|
|
213
|
+
export {
|
|
214
|
+
/** Durably seal a channel's token into the encrypted credential store (survives reboot). */
|
|
215
|
+
sealChannelToken,
|
|
216
|
+
/** Read a channel's durably-sealed token (""→ none) at start time, no agent context needed. */
|
|
217
|
+
readSealedChannelToken,
|
|
218
|
+
/** Provider key a channel's token is sealed under (`channel:<id>`). */
|
|
219
|
+
channelSecretProvider, } from "./channel-secrets.js";
|
|
220
|
+
/* ───────────────────────── pipeline: the shared inbound engine ───────────────────────── */
|
|
221
|
+
export {
|
|
222
|
+
/**
|
|
223
|
+
* Run ONE inbound message end-to-end through the shared pipeline (media + reply
|
|
224
|
+
* note synthesis → access gate → mark-read → approval-callback + approval-reply
|
|
225
|
+
* intercept → abort triggers → channel command → 8-tier route → last-channel pin
|
|
226
|
+
* → optional debounce → dispatchTurn → reply). Never throws. Both the legacy
|
|
227
|
+
* manager and every multi-account plugin call THIS so the safety surface is
|
|
228
|
+
* identical on every channel.
|
|
229
|
+
*/
|
|
230
|
+
runChannelInboundPipeline,
|
|
231
|
+
/** Build a fresh per-channel-instance pipeline context (per account on the plugin path). */
|
|
232
|
+
createInboundPipelineContext,
|
|
233
|
+
/**
|
|
234
|
+
* Build the bundled built-in channel commands (`/help`, `/status`, `/allowlist`,
|
|
235
|
+
* `/agent`, `/agents`, `/whoami`, `/org`) an operator can DM to admin the bot.
|
|
236
|
+
* Feed the result into your `commandMap` AND into a native command menu.
|
|
237
|
+
*/
|
|
238
|
+
buildBundledCommands,
|
|
239
|
+
/** Disambiguating lane key for inflight + pending maps ((adapter, account, conversation, thread)). */
|
|
240
|
+
laneKey, } from "./inbound-pipeline.js";
|
|
241
|
+
/* ───────────────────────── approvals: the channel approval router ───────────────────────── */
|
|
242
|
+
export {
|
|
243
|
+
/**
|
|
244
|
+
* Register a (per-account) dispatcher so an exec-gate approval prompt raised by
|
|
245
|
+
* a channel-routed turn lands IN that conversation. Pass `(channelId, accountId,
|
|
246
|
+
* dispatcher)` on the multi-account path so each account routes to itself.
|
|
247
|
+
*/
|
|
248
|
+
registerChannelApprovalDispatcher,
|
|
249
|
+
/** Drop a channel's (per-account) dispatcher + deny its in-flight prompts on stop. */
|
|
250
|
+
removeChannelApprovalDispatcher,
|
|
251
|
+
/** Send an approval prompt via the channel + register the pending entry (called by the bridge). */
|
|
252
|
+
dispatchChannelApproval,
|
|
253
|
+
/** Intercept a yes/no TEXT reply as the answer to a pending approval for this peer. */
|
|
254
|
+
tryConsumeChannelApprovalReply,
|
|
255
|
+
/** Intercept an inline-BUTTON press (callback_query) as the answer to a pending approval. */
|
|
256
|
+
tryConsumeChannelApprovalCallback,
|
|
257
|
+
/** Cancel a pending approval by request id (e.g. on session abort). */
|
|
258
|
+
cancelChannelApprovalById,
|
|
259
|
+
/** Diagnostic — list registered dispatcher keys. */
|
|
260
|
+
listChannelApprovalDispatchers,
|
|
261
|
+
/** Diagnostic — snapshot of pending channel approvals. */
|
|
262
|
+
listPendingChannelApprovals, } from "./approval-router.js";
|
|
263
|
+
/* ───────────────────────── last-channel: the announce-target pin ───────────────────────── */
|
|
264
|
+
export {
|
|
265
|
+
/**
|
|
266
|
+
* Pin THIS channel as the agent's most-recently-active (called by the pipeline
|
|
267
|
+
* on every admitted inbound). A cron's announce-delivery reads it as the
|
|
268
|
+
* last-resort target when no explicit channel was set. The pipeline already
|
|
269
|
+
* calls this for you; a channel only calls it directly for a bespoke surface.
|
|
270
|
+
*/
|
|
271
|
+
recordLastChannelForAgent,
|
|
272
|
+
/** Read the agent's last-recorded channel (undefined when no channel activity yet). */
|
|
273
|
+
getLastChannelForAgent, } from "./last-channel.js";
|
|
274
|
+
//# sourceMappingURL=sdk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk.js","sourceRoot":"","sources":["../../../src/agents/channels/sdk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgKG;AAEH,4EAA4E;AAE5E,OAAO;AACN,gHAAgH;AAChH,YAAY,GACZ,MAAM,wBAAwB,CAAC;AA+DhC,0FAA0F;AAE1F,OAAO;AACN;;;;;GAKG;AACH,SAAS,GACT,MAAM,qBAAqB,CAAC;AAE7B,uFAAuF;AAEvF,OAAO;AACN,wEAAwE;AACxE,iBAAiB,GACjB,MAAM,aAAa,CAAC;AAGrB,6FAA6F;AAE7F,OAAO;AACN;;;;GAIG;AACH,yBAAyB,GACzB,MAAM,oCAAoC,CAAC;AAG5C,0FAA0F;AAE1F,OAAO;AACN,8EAA8E;AAC9E,qBAAqB,GACrB,MAAM,mCAAmC,CAAC;AAG3C,0FAA0F;AAE1F,OAAO;AACN;;;;;GAKG;AACH,gBAAgB,GAChB,MAAM,cAAc,CAAC;AAGtB,+FAA+F;AAE/F,OAAO;AACN,oGAAoG;AACpG,sBAAsB;AACtB,mGAAmG;AACnG,sBAAsB;AACtB,yEAAyE;AACzE,oBAAoB;AACpB,6FAA6F;AAC7F,4BAA4B;AAC5B,gEAAgE;AAChE,2BAA2B,GAC3B,MAAM,8BAA8B,CAAC;AAQtC,6FAA6F;AAE7F,OAAO;AACN,4FAA4F;AAC5F,gBAAgB;AAChB,+FAA+F;AAC/F,sBAAsB;AACtB,uEAAuE;AACvE,qBAAqB,GACrB,MAAM,sBAAsB,CAAC;AAsF9B,6FAA6F;AAE7F,OAAO;AACN;;;;;;;GAOG;AACH,yBAAyB;AACzB,4FAA4F;AAC5F,4BAA4B;AAC5B;;;;GAIG;AACH,oBAAoB;AACpB,sGAAsG;AACtG,OAAO,GACP,MAAM,uBAAuB,CAAC;AAY/B,gGAAgG;AAEhG,OAAO;AACN;;;;GAIG;AACH,iCAAiC;AACjC,sFAAsF;AACtF,+BAA+B;AAC/B,mGAAmG;AACnG,uBAAuB;AACvB,uFAAuF;AACvF,8BAA8B;AAC9B,6FAA6F;AAC7F,iCAAiC;AACjC,uEAAuE;AACvE,yBAAyB;AACzB,oDAAoD;AACpD,8BAA8B;AAC9B,0DAA0D;AAC1D,2BAA2B,GAC3B,MAAM,sBAAsB,CAAC;AA2B9B,+FAA+F;AAE/F,OAAO;AACN;;;;;GAKG;AACH,yBAAyB;AACzB,uFAAuF;AACvF,sBAAsB,GACtB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram config-shape helpers (single-account v1; multi-account aware shape).
|
|
3
|
+
*
|
|
4
|
+
* Brigade's Telegram config is TOKEN-based (no QR/OAuth pairing — the operator
|
|
5
|
+
* pastes a Bot API token from @BotFather). Two shapes are recognised so the
|
|
6
|
+
* surface lines up with WhatsApp's `account-config.ts`:
|
|
7
|
+
*
|
|
8
|
+
* Legacy (single-account):
|
|
9
|
+
* channels.telegram = { enabled: true, botToken: "123:ABC", verbose?: boolean }
|
|
10
|
+
*
|
|
11
|
+
* Multi-account (later follow-up — the shape is honoured now, v1 ships one):
|
|
12
|
+
* channels.telegram = {
|
|
13
|
+
* enabled: true,
|
|
14
|
+
* accounts: [
|
|
15
|
+
* { id: "main", botToken: "111:AAA" },
|
|
16
|
+
* { id: "ops", botToken: "222:BBB" },
|
|
17
|
+
* ],
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* A legacy config with no `accounts[]` reads as `[{ id: "default" }]`.
|
|
21
|
+
*
|
|
22
|
+
* Token resolution: a value of the form `${VAR}` is resolved against
|
|
23
|
+
* `process.env[VAR]` (mirrors Brigade's config secret-ref convention, so a
|
|
24
|
+
* token never has to be committed as a literal). When no token is configured
|
|
25
|
+
* at all, the `TELEGRAM_BOT_TOKEN` environment variable is the last-resort
|
|
26
|
+
* fallback. NOTE: Brigade's config loader already resolves `${VAR}` leaves on
|
|
27
|
+
* read (`resolveSecretsInPlace`), so by the time the adapter reads config the
|
|
28
|
+
* ref is usually already expanded — the explicit resolution here is defensive
|
|
29
|
+
* for callers (CLI/tests) that read raw config and for the env fallback.
|
|
30
|
+
*/
|
|
31
|
+
import type { BrigadeConfig } from "../../../config/io.js";
|
|
32
|
+
declare const CHANNEL_ID = "telegram";
|
|
33
|
+
declare const DEFAULT_ACCOUNT_ID = "default";
|
|
34
|
+
/** The env var consulted as a last-resort token fallback. */
|
|
35
|
+
declare const TOKEN_ENV_VAR = "TELEGRAM_BOT_TOKEN";
|
|
36
|
+
/** Resolved per-account info — what the adapter runtime reads. */
|
|
37
|
+
export interface ResolvedTelegramAccount {
|
|
38
|
+
accountId: string;
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
/** Bot API token, fully resolved (`${VAR}` + env fallback applied), or `""` when unset. */
|
|
41
|
+
botToken: string;
|
|
42
|
+
verbose: boolean;
|
|
43
|
+
}
|
|
44
|
+
/** Is the Telegram channel switched on at all (any shape)? */
|
|
45
|
+
export declare function telegramChannelEnabled(cfg: BrigadeConfig): boolean;
|
|
46
|
+
/** List configured account ids. Legacy single-account configs surface `["default"]`. */
|
|
47
|
+
export declare function listTelegramAccountIds(cfg: BrigadeConfig): string[];
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the Bot API token for an account. Precedence:
|
|
50
|
+
* 1. The per-account `botToken` (multi-account shape), `${VAR}`-resolved.
|
|
51
|
+
* 2. The top-level `channels.telegram.botToken` (legacy shape), `${VAR}`-resolved.
|
|
52
|
+
* 3. The DURABLE sealed channel token (written by `connect_channel`) — this is
|
|
53
|
+
* the source that survives a gateway reboot, when the live env + `${VAR}`
|
|
54
|
+
* ref have evaporated.
|
|
55
|
+
* 4. The `TELEGRAM_BOT_TOKEN` env var (last-resort fallback).
|
|
56
|
+
* Returns `""` when no token can be resolved.
|
|
57
|
+
*
|
|
58
|
+
* Note the sealed token is consulted AFTER the config refs: an explicit config
|
|
59
|
+
* `${VAR}` / literal that resolves in THIS process wins (so a live env set by
|
|
60
|
+
* `connect_channel` is used immediately), and the durable seal is the fallback
|
|
61
|
+
* that keeps the channel authenticated across restarts.
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveTelegramBotToken(cfg: BrigadeConfig, accountId?: string | null, env?: NodeJS.ProcessEnv): string;
|
|
64
|
+
/** Resolve a per-account view of the config (defaults + token resolution filled in). */
|
|
65
|
+
export declare function resolveTelegramAccount(cfg: BrigadeConfig, accountId?: string | null, env?: NodeJS.ProcessEnv): ResolvedTelegramAccount;
|
|
66
|
+
/** Resolved Telegram transport config (mode + webhook details). */
|
|
67
|
+
export interface TelegramWebhookConfig {
|
|
68
|
+
/** `"polling"` (default) or `"webhook"`. */
|
|
69
|
+
mode: "polling" | "webhook";
|
|
70
|
+
/** Public base URL Telegram POSTs to (webhook mode). `""` when unset. */
|
|
71
|
+
url: string;
|
|
72
|
+
/** Gateway route path (default `/telegram/webhook`). */
|
|
73
|
+
path: string;
|
|
74
|
+
/** Secret token verified on the inbound webhook header. `""` when unset. */
|
|
75
|
+
secretToken: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolve the Telegram transport config. Defaults to `polling` (Brigade is
|
|
79
|
+
* local-first); `webhook` is opt-in via `channels.telegram.mode: "webhook"`.
|
|
80
|
+
* `${VAR}` refs in the secret token are env-resolved (it's a secret).
|
|
81
|
+
*/
|
|
82
|
+
export declare function telegramWebhookConfig(cfg: BrigadeConfig, env?: NodeJS.ProcessEnv): TelegramWebhookConfig;
|
|
83
|
+
/** True when forum-topic auto-labeling is enabled (`channels.telegram.autoLabelTopics`). */
|
|
84
|
+
export declare function telegramAutoLabelTopics(cfg: BrigadeConfig): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Resolve the idle-thread-session TTL in ms, or `null` when unset / disabled.
|
|
87
|
+
* Accepts a number (ms) or a duration string (`"6h"`, `"30m"`, …). The cron
|
|
88
|
+
* session-reaper uses this to age out idle Telegram thread sessions.
|
|
89
|
+
*/
|
|
90
|
+
export declare function telegramThreadIdleTtlMs(cfg: BrigadeConfig): number | null;
|
|
91
|
+
export { CHANNEL_ID as TELEGRAM_CHANNEL_ID, DEFAULT_ACCOUNT_ID as TELEGRAM_DEFAULT_ACCOUNT_ID, TOKEN_ENV_VAR as TELEGRAM_BOT_TOKEN_ENV_VAR, };
|
|
92
|
+
//# sourceMappingURL=account-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-config.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/telegram/account-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D,QAAA,MAAM,UAAU,aAAa,CAAC;AAC9B,QAAA,MAAM,kBAAkB,YAAY,CAAC;AAErC,6DAA6D;AAC7D,QAAA,MAAM,aAAa,uBAAuB,CAAC;AAwC3C,kEAAkE;AAClE,MAAM,WAAW,uBAAuB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,2FAA2F;IAC3F,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CACjB;AAqBD,8DAA8D;AAC9D,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAElE;AAED,wFAAwF;AACxF,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,EAAE,CAgBnE;AAeD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACtC,GAAG,EAAE,aAAa,EAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EACzB,GAAG,GAAE,MAAM,CAAC,UAAwB,GAClC,MAAM,CAWR;AAED,wFAAwF;AACxF,wBAAgB,sBAAsB,CACrC,GAAG,EAAE,aAAa,EAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EACzB,GAAG,GAAE,MAAM,CAAC,UAAwB,GAClC,uBAAuB,CAWzB;AAOD,mEAAmE;AACnE,MAAM,WAAW,qBAAqB;IACrC,4CAA4C;IAC5C,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAC5B,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAC;IACZ,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACpC,GAAG,EAAE,aAAa,EAClB,GAAG,GAAE,MAAM,CAAC,UAAwB,GAClC,qBAAqB,CAYvB;AAED,4FAA4F;AAC5F,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAczE;AAED,OAAO,EACN,UAAU,IAAI,mBAAmB,EACjC,kBAAkB,IAAI,2BAA2B,EACjD,aAAa,IAAI,0BAA0B,GAC3C,CAAC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram config-shape helpers (single-account v1; multi-account aware shape).
|
|
3
|
+
*
|
|
4
|
+
* Brigade's Telegram config is TOKEN-based (no QR/OAuth pairing — the operator
|
|
5
|
+
* pastes a Bot API token from @BotFather). Two shapes are recognised so the
|
|
6
|
+
* surface lines up with WhatsApp's `account-config.ts`:
|
|
7
|
+
*
|
|
8
|
+
* Legacy (single-account):
|
|
9
|
+
* channels.telegram = { enabled: true, botToken: "123:ABC", verbose?: boolean }
|
|
10
|
+
*
|
|
11
|
+
* Multi-account (later follow-up — the shape is honoured now, v1 ships one):
|
|
12
|
+
* channels.telegram = {
|
|
13
|
+
* enabled: true,
|
|
14
|
+
* accounts: [
|
|
15
|
+
* { id: "main", botToken: "111:AAA" },
|
|
16
|
+
* { id: "ops", botToken: "222:BBB" },
|
|
17
|
+
* ],
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* A legacy config with no `accounts[]` reads as `[{ id: "default" }]`.
|
|
21
|
+
*
|
|
22
|
+
* Token resolution: a value of the form `${VAR}` is resolved against
|
|
23
|
+
* `process.env[VAR]` (mirrors Brigade's config secret-ref convention, so a
|
|
24
|
+
* token never has to be committed as a literal). When no token is configured
|
|
25
|
+
* at all, the `TELEGRAM_BOT_TOKEN` environment variable is the last-resort
|
|
26
|
+
* fallback. NOTE: Brigade's config loader already resolves `${VAR}` leaves on
|
|
27
|
+
* read (`resolveSecretsInPlace`), so by the time the adapter reads config the
|
|
28
|
+
* ref is usually already expanded — the explicit resolution here is defensive
|
|
29
|
+
* for callers (CLI/tests) that read raw config and for the env fallback.
|
|
30
|
+
*/
|
|
31
|
+
import { readSealedChannelToken } from "../channel-secrets.js";
|
|
32
|
+
const CHANNEL_ID = "telegram";
|
|
33
|
+
const DEFAULT_ACCOUNT_ID = "default";
|
|
34
|
+
/** The env var consulted as a last-resort token fallback. */
|
|
35
|
+
const TOKEN_ENV_VAR = "TELEGRAM_BOT_TOKEN";
|
|
36
|
+
/** `${VAR}` secret-ref form — identical to `config/io.ts`'s SECRET_REF_PATTERN. */
|
|
37
|
+
const SECRET_REF_PATTERN = /^\$\{([A-Z_][A-Z0-9_]*)\}$/;
|
|
38
|
+
/** Read `channels.telegram` loosely (schema keeps it open). */
|
|
39
|
+
function telegramChannelConfig(cfg) {
|
|
40
|
+
return cfg.channels?.[CHANNEL_ID];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a single token-ish string: a `${VAR}` ref expands against
|
|
44
|
+
* `process.env`; any other non-empty string passes through verbatim; empty /
|
|
45
|
+
* missing returns "".
|
|
46
|
+
*/
|
|
47
|
+
function resolveTokenRef(raw, env) {
|
|
48
|
+
if (typeof raw !== "string")
|
|
49
|
+
return "";
|
|
50
|
+
const trimmed = raw.trim();
|
|
51
|
+
if (!trimmed)
|
|
52
|
+
return "";
|
|
53
|
+
const m = SECRET_REF_PATTERN.exec(trimmed);
|
|
54
|
+
if (m && m[1])
|
|
55
|
+
return (env[m[1]] ?? "").trim();
|
|
56
|
+
return trimmed;
|
|
57
|
+
}
|
|
58
|
+
/** Is the Telegram channel switched on at all (any shape)? */
|
|
59
|
+
export function telegramChannelEnabled(cfg) {
|
|
60
|
+
return telegramChannelConfig(cfg)?.enabled === true;
|
|
61
|
+
}
|
|
62
|
+
/** List configured account ids. Legacy single-account configs surface `["default"]`. */
|
|
63
|
+
export function listTelegramAccountIds(cfg) {
|
|
64
|
+
const slot = telegramChannelConfig(cfg);
|
|
65
|
+
if (!slot || slot.enabled !== true)
|
|
66
|
+
return [];
|
|
67
|
+
const accounts = Array.isArray(slot.accounts) ? slot.accounts : undefined;
|
|
68
|
+
if (!accounts || accounts.length === 0)
|
|
69
|
+
return [DEFAULT_ACCOUNT_ID];
|
|
70
|
+
const ids = [];
|
|
71
|
+
const seen = new Set();
|
|
72
|
+
for (const entry of accounts) {
|
|
73
|
+
const id = typeof entry?.id === "string" ? entry.id.trim() : "";
|
|
74
|
+
if (!id || seen.has(id))
|
|
75
|
+
continue;
|
|
76
|
+
seen.add(id);
|
|
77
|
+
ids.push(id);
|
|
78
|
+
}
|
|
79
|
+
// A half-typed `accounts:[]` still degrades to the default account so the
|
|
80
|
+
// channel isn't silently disabled.
|
|
81
|
+
return ids.length === 0 ? [DEFAULT_ACCOUNT_ID] : ids;
|
|
82
|
+
}
|
|
83
|
+
/** Look up the raw account entry from config (or null when missing). */
|
|
84
|
+
function findAccountEntry(cfg, accountId) {
|
|
85
|
+
const slot = telegramChannelConfig(cfg);
|
|
86
|
+
if (!slot)
|
|
87
|
+
return null;
|
|
88
|
+
const accounts = Array.isArray(slot.accounts) ? slot.accounts : undefined;
|
|
89
|
+
if (!accounts)
|
|
90
|
+
return null;
|
|
91
|
+
for (const entry of accounts) {
|
|
92
|
+
const id = typeof entry?.id === "string" ? entry.id.trim() : "";
|
|
93
|
+
if (id === accountId)
|
|
94
|
+
return entry;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Resolve the Bot API token for an account. Precedence:
|
|
100
|
+
* 1. The per-account `botToken` (multi-account shape), `${VAR}`-resolved.
|
|
101
|
+
* 2. The top-level `channels.telegram.botToken` (legacy shape), `${VAR}`-resolved.
|
|
102
|
+
* 3. The DURABLE sealed channel token (written by `connect_channel`) — this is
|
|
103
|
+
* the source that survives a gateway reboot, when the live env + `${VAR}`
|
|
104
|
+
* ref have evaporated.
|
|
105
|
+
* 4. The `TELEGRAM_BOT_TOKEN` env var (last-resort fallback).
|
|
106
|
+
* Returns `""` when no token can be resolved.
|
|
107
|
+
*
|
|
108
|
+
* Note the sealed token is consulted AFTER the config refs: an explicit config
|
|
109
|
+
* `${VAR}` / literal that resolves in THIS process wins (so a live env set by
|
|
110
|
+
* `connect_channel` is used immediately), and the durable seal is the fallback
|
|
111
|
+
* that keeps the channel authenticated across restarts.
|
|
112
|
+
*/
|
|
113
|
+
export function resolveTelegramBotToken(cfg, accountId, env = process.env) {
|
|
114
|
+
const id = accountId?.trim() || DEFAULT_ACCOUNT_ID;
|
|
115
|
+
const slot = telegramChannelConfig(cfg);
|
|
116
|
+
const entry = findAccountEntry(cfg, id);
|
|
117
|
+
const perAccount = resolveTokenRef(entry?.botToken, env);
|
|
118
|
+
if (perAccount)
|
|
119
|
+
return perAccount;
|
|
120
|
+
const topLevel = resolveTokenRef(slot?.botToken, env);
|
|
121
|
+
if (topLevel)
|
|
122
|
+
return topLevel;
|
|
123
|
+
const sealed = readSealedChannelToken(CHANNEL_ID);
|
|
124
|
+
if (sealed)
|
|
125
|
+
return sealed;
|
|
126
|
+
return (env[TOKEN_ENV_VAR] ?? "").trim();
|
|
127
|
+
}
|
|
128
|
+
/** Resolve a per-account view of the config (defaults + token resolution filled in). */
|
|
129
|
+
export function resolveTelegramAccount(cfg, accountId, env = process.env) {
|
|
130
|
+
const slot = telegramChannelConfig(cfg);
|
|
131
|
+
const id = accountId?.trim() || DEFAULT_ACCOUNT_ID;
|
|
132
|
+
const entry = findAccountEntry(cfg, id);
|
|
133
|
+
const enabled = entry?.enabled !== false && slot?.enabled === true;
|
|
134
|
+
return {
|
|
135
|
+
accountId: id,
|
|
136
|
+
enabled,
|
|
137
|
+
botToken: resolveTelegramBotToken(cfg, id, env),
|
|
138
|
+
verbose: slot?.verbose === true,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/* ───────────────────────── transport / lifecycle config ───────────────────────── */
|
|
142
|
+
/** Default gateway path the webhook transport registers + points `setWebhook` at. */
|
|
143
|
+
const DEFAULT_WEBHOOK_PATH = "/telegram/webhook";
|
|
144
|
+
/**
|
|
145
|
+
* Resolve the Telegram transport config. Defaults to `polling` (Brigade is
|
|
146
|
+
* local-first); `webhook` is opt-in via `channels.telegram.mode: "webhook"`.
|
|
147
|
+
* `${VAR}` refs in the secret token are env-resolved (it's a secret).
|
|
148
|
+
*/
|
|
149
|
+
export function telegramWebhookConfig(cfg, env = process.env) {
|
|
150
|
+
const slot = telegramChannelConfig(cfg);
|
|
151
|
+
const rawMode = typeof slot?.mode === "string" ? slot.mode.trim().toLowerCase() : "";
|
|
152
|
+
const mode = rawMode === "webhook" ? "webhook" : "polling";
|
|
153
|
+
const wh = slot?.webhook ?? {};
|
|
154
|
+
const path = typeof wh.path === "string" && wh.path.trim() ? wh.path.trim() : DEFAULT_WEBHOOK_PATH;
|
|
155
|
+
return {
|
|
156
|
+
mode,
|
|
157
|
+
url: typeof wh.url === "string" ? wh.url.trim() : "",
|
|
158
|
+
path: path.startsWith("/") ? path : `/${path}`,
|
|
159
|
+
secretToken: resolveTokenRef(wh.secretToken, env),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/** True when forum-topic auto-labeling is enabled (`channels.telegram.autoLabelTopics`). */
|
|
163
|
+
export function telegramAutoLabelTopics(cfg) {
|
|
164
|
+
return telegramChannelConfig(cfg)?.autoLabelTopics === true;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Resolve the idle-thread-session TTL in ms, or `null` when unset / disabled.
|
|
168
|
+
* Accepts a number (ms) or a duration string (`"6h"`, `"30m"`, …). The cron
|
|
169
|
+
* session-reaper uses this to age out idle Telegram thread sessions.
|
|
170
|
+
*/
|
|
171
|
+
export function telegramThreadIdleTtlMs(cfg) {
|
|
172
|
+
const raw = telegramChannelConfig(cfg)?.threadIdleTtlMs;
|
|
173
|
+
if (typeof raw === "number")
|
|
174
|
+
return raw > 0 ? raw : null;
|
|
175
|
+
if (typeof raw !== "string")
|
|
176
|
+
return null;
|
|
177
|
+
const trimmed = raw.trim();
|
|
178
|
+
if (!trimmed)
|
|
179
|
+
return null;
|
|
180
|
+
const m = /^(\d+)\s*(s|m|h|d|w)?$/i.exec(trimmed);
|
|
181
|
+
if (!m)
|
|
182
|
+
return null;
|
|
183
|
+
const n = Number(m[1]);
|
|
184
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
185
|
+
return null;
|
|
186
|
+
const unit = (m[2] ?? "ms").toLowerCase();
|
|
187
|
+
const mult = { s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000, w: 604_800_000, ms: 1 };
|
|
188
|
+
const factor = mult[unit] ?? 1;
|
|
189
|
+
return n * factor;
|
|
190
|
+
}
|
|
191
|
+
export { CHANNEL_ID as TELEGRAM_CHANNEL_ID, DEFAULT_ACCOUNT_ID as TELEGRAM_DEFAULT_ACCOUNT_ID, TOKEN_ENV_VAR as TELEGRAM_BOT_TOKEN_ENV_VAR, };
|
|
192
|
+
//# sourceMappingURL=account-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-config.js","sourceRoot":"","sources":["../../../../src/agents/channels/telegram/account-config.ts"],"names":[],"mappings":"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"}
|