@spinabot/brigade 1.3.2 → 1.4.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/README.md +54 -10
- package/convex/config.d.ts +2 -2
- package/convex/extensions.d.ts +2 -2
- package/convex/logs.d.ts +2 -2
- package/convex/memory.d.ts +7 -7
- package/convex/messages.d.ts +4 -4
- package/convex/schema.d.ts +17 -17
- package/convex/subagents.d.ts +12 -12
- package/dist/agents/agent-loop.d.ts +1 -0
- package/dist/agents/agent-loop.d.ts.map +1 -1
- package/dist/agents/agent-loop.js +1 -1
- package/dist/agents/agent-loop.js.map +1 -1
- package/dist/agents/channels/access-control/format-allow-from.d.ts +50 -0
- package/dist/agents/channels/access-control/format-allow-from.d.ts.map +1 -0
- package/dist/agents/channels/access-control/format-allow-from.js +64 -0
- package/dist/agents/channels/access-control/format-allow-from.js.map +1 -0
- package/dist/agents/channels/access-control/index.d.ts +2 -1
- package/dist/agents/channels/access-control/index.d.ts.map +1 -1
- package/dist/agents/channels/access-control/index.js +2 -1
- package/dist/agents/channels/access-control/index.js.map +1 -1
- package/dist/agents/channels/access-control/store.d.ts +15 -0
- package/dist/agents/channels/access-control/store.d.ts.map +1 -1
- package/dist/agents/channels/access-control/store.js +44 -1
- package/dist/agents/channels/access-control/store.js.map +1 -1
- package/dist/agents/channels/bundled-channel-metas.d.ts +26 -0
- package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -0
- package/dist/agents/channels/bundled-channel-metas.js +44 -0
- package/dist/agents/channels/bundled-channel-metas.js.map +1 -0
- package/dist/agents/channels/channel-messaging-registry.d.ts +130 -0
- package/dist/agents/channels/channel-messaging-registry.d.ts.map +1 -0
- package/dist/agents/channels/channel-messaging-registry.js +211 -0
- package/dist/agents/channels/channel-messaging-registry.js.map +1 -0
- package/dist/agents/channels/channel-meta-registry.d.ts +60 -0
- package/dist/agents/channels/channel-meta-registry.d.ts.map +1 -0
- package/dist/agents/channels/channel-meta-registry.js +128 -0
- package/dist/agents/channels/channel-meta-registry.js.map +1 -0
- package/dist/agents/channels/channel-security-registry.d.ts +138 -0
- package/dist/agents/channels/channel-security-registry.d.ts.map +1 -0
- package/dist/agents/channels/channel-security-registry.js +265 -0
- package/dist/agents/channels/channel-security-registry.js.map +1 -0
- package/dist/agents/channels/exposure.d.ts +44 -0
- package/dist/agents/channels/exposure.d.ts.map +1 -0
- package/dist/agents/channels/exposure.js +48 -0
- package/dist/agents/channels/exposure.js.map +1 -0
- package/dist/agents/channels/general-callback.d.ts +25 -0
- package/dist/agents/channels/general-callback.d.ts.map +1 -0
- package/dist/agents/channels/general-callback.js +31 -0
- package/dist/agents/channels/general-callback.js.map +1 -0
- package/dist/agents/channels/inbound-pipeline.d.ts +9 -0
- package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
- package/dist/agents/channels/inbound-pipeline.js +429 -39
- package/dist/agents/channels/inbound-pipeline.js.map +1 -1
- package/dist/agents/channels/markdown-capability.d.ts +44 -0
- package/dist/agents/channels/markdown-capability.d.ts.map +1 -0
- package/dist/agents/channels/markdown-capability.js +66 -0
- package/dist/agents/channels/markdown-capability.js.map +1 -0
- package/dist/agents/channels/sdk.d.ts +170 -10
- package/dist/agents/channels/sdk.d.ts.map +1 -1
- package/dist/agents/channels/sdk.js +138 -6
- package/dist/agents/channels/sdk.js.map +1 -1
- package/dist/agents/channels/telegram/account-config.d.ts +41 -0
- package/dist/agents/channels/telegram/account-config.d.ts.map +1 -1
- package/dist/agents/channels/telegram/account-config.js +79 -0
- package/dist/agents/channels/telegram/account-config.js.map +1 -1
- package/dist/agents/channels/telegram/adapter.d.ts +6 -0
- package/dist/agents/channels/telegram/adapter.d.ts.map +1 -1
- package/dist/agents/channels/telegram/adapter.js +178 -6
- package/dist/agents/channels/telegram/adapter.js.map +1 -1
- package/dist/agents/channels/telegram/allowed-updates.d.ts +14 -5
- package/dist/agents/channels/telegram/allowed-updates.d.ts.map +1 -1
- package/dist/agents/channels/telegram/allowed-updates.js +8 -4
- package/dist/agents/channels/telegram/allowed-updates.js.map +1 -1
- package/dist/agents/channels/telegram/connection.d.ts +108 -1
- package/dist/agents/channels/telegram/connection.d.ts.map +1 -1
- package/dist/agents/channels/telegram/connection.js +219 -3
- package/dist/agents/channels/telegram/connection.js.map +1 -1
- package/dist/agents/channels/telegram/draft-stream.d.ts +98 -0
- package/dist/agents/channels/telegram/draft-stream.d.ts.map +1 -0
- package/dist/agents/channels/telegram/draft-stream.js +222 -0
- package/dist/agents/channels/telegram/draft-stream.js.map +1 -0
- package/dist/agents/channels/telegram/inbound-extras.d.ts +10 -1
- package/dist/agents/channels/telegram/inbound-extras.d.ts.map +1 -1
- package/dist/agents/channels/telegram/inbound-extras.js +66 -0
- package/dist/agents/channels/telegram/inbound-extras.js.map +1 -1
- package/dist/agents/channels/telegram/inline-keyboard.d.ts +36 -0
- package/dist/agents/channels/telegram/inline-keyboard.d.ts.map +1 -0
- package/dist/agents/channels/telegram/inline-keyboard.js +62 -0
- package/dist/agents/channels/telegram/inline-keyboard.js.map +1 -0
- package/dist/agents/channels/telegram/plugin.d.ts.map +1 -1
- package/dist/agents/channels/telegram/plugin.js +7 -11
- package/dist/agents/channels/telegram/plugin.js.map +1 -1
- package/dist/agents/channels/telegram/reasoning-lane.d.ts +41 -0
- package/dist/agents/channels/telegram/reasoning-lane.d.ts.map +1 -0
- package/dist/agents/channels/telegram/reasoning-lane.js +67 -0
- package/dist/agents/channels/telegram/reasoning-lane.js.map +1 -0
- package/dist/agents/channels/telegram/socks-dispatcher.d.ts +32 -0
- package/dist/agents/channels/telegram/socks-dispatcher.d.ts.map +1 -0
- package/dist/agents/channels/telegram/socks-dispatcher.js +97 -0
- package/dist/agents/channels/telegram/socks-dispatcher.js.map +1 -0
- package/dist/agents/channels/types.adapters.d.ts +137 -4
- package/dist/agents/channels/types.adapters.d.ts.map +1 -1
- package/dist/agents/channels/types.adapters.js +2 -2
- package/dist/agents/channels/types.core.d.ts +26 -2
- package/dist/agents/channels/types.core.d.ts.map +1 -1
- package/dist/agents/channels/types.plugin.d.ts +25 -7
- package/dist/agents/channels/types.plugin.d.ts.map +1 -1
- package/dist/agents/channels/types.plugin.js +6 -5
- package/dist/agents/channels/types.plugin.js.map +1 -1
- package/dist/agents/channels/whatsapp/adapter.d.ts +8 -0
- package/dist/agents/channels/whatsapp/adapter.d.ts.map +1 -1
- package/dist/agents/channels/whatsapp/adapter.js +7 -4
- package/dist/agents/channels/whatsapp/adapter.js.map +1 -1
- package/dist/agents/channels/whatsapp/connection.d.ts +24 -2
- package/dist/agents/channels/whatsapp/connection.d.ts.map +1 -1
- package/dist/agents/channels/whatsapp/connection.js +26 -5
- package/dist/agents/channels/whatsapp/connection.js.map +1 -1
- package/dist/agents/channels/whatsapp/plugin.d.ts.map +1 -1
- package/dist/agents/channels/whatsapp/plugin.js +6 -10
- package/dist/agents/channels/whatsapp/plugin.js.map +1 -1
- package/dist/agents/extensions/activation-planner.d.ts +125 -0
- package/dist/agents/extensions/activation-planner.d.ts.map +1 -0
- package/dist/agents/extensions/activation-planner.js +221 -0
- package/dist/agents/extensions/activation-planner.js.map +1 -0
- package/dist/agents/extensions/diagnose.d.ts +84 -0
- package/dist/agents/extensions/diagnose.d.ts.map +1 -0
- package/dist/agents/extensions/diagnose.js +123 -0
- package/dist/agents/extensions/diagnose.js.map +1 -0
- package/dist/agents/extensions/discovery.d.ts +85 -7
- package/dist/agents/extensions/discovery.d.ts.map +1 -1
- package/dist/agents/extensions/discovery.js +200 -15
- package/dist/agents/extensions/discovery.js.map +1 -1
- package/dist/agents/extensions/index.d.ts +3 -2
- package/dist/agents/extensions/index.d.ts.map +1 -1
- package/dist/agents/extensions/index.js +3 -2
- package/dist/agents/extensions/index.js.map +1 -1
- package/dist/agents/extensions/install-scan.d.ts +63 -0
- package/dist/agents/extensions/install-scan.d.ts.map +1 -0
- package/dist/agents/extensions/install-scan.js +201 -0
- package/dist/agents/extensions/install-scan.js.map +1 -0
- package/dist/agents/extensions/install.d.ts +135 -0
- package/dist/agents/extensions/install.d.ts.map +1 -0
- package/dist/agents/extensions/install.js +414 -0
- package/dist/agents/extensions/install.js.map +1 -0
- package/dist/agents/extensions/loader.d.ts +13 -2
- package/dist/agents/extensions/loader.d.ts.map +1 -1
- package/dist/agents/extensions/loader.js +126 -13
- package/dist/agents/extensions/loader.js.map +1 -1
- package/dist/agents/extensions/registry.d.ts +109 -0
- package/dist/agents/extensions/registry.d.ts.map +1 -1
- package/dist/agents/extensions/registry.js +172 -0
- package/dist/agents/extensions/registry.js.map +1 -1
- package/dist/agents/extensions/sdk-alias.d.ts +45 -0
- package/dist/agents/extensions/sdk-alias.d.ts.map +1 -0
- package/dist/agents/extensions/sdk-alias.js +94 -0
- package/dist/agents/extensions/sdk-alias.js.map +1 -0
- package/dist/agents/extensions/types.d.ts +155 -1
- package/dist/agents/extensions/types.d.ts.map +1 -1
- package/dist/agents/extensions/types.js.map +1 -1
- package/dist/agents/tools/composio-tool.d.ts +9 -1
- package/dist/agents/tools/composio-tool.d.ts.map +1 -1
- package/dist/agents/tools/composio-tool.js +68 -4
- package/dist/agents/tools/composio-tool.js.map +1 -1
- package/dist/agents/tools/message-action-tool.d.ts +6 -1
- package/dist/agents/tools/message-action-tool.d.ts.map +1 -1
- package/dist/agents/tools/message-action-tool.js +52 -2
- package/dist/agents/tools/message-action-tool.js.map +1 -1
- package/dist/agents/tools/send-message-tool.d.ts +1 -0
- package/dist/agents/tools/send-message-tool.d.ts.map +1 -1
- package/dist/agents/tools/send-message-tool.js +56 -1
- package/dist/agents/tools/send-message-tool.js.map +1 -1
- package/dist/buildstamp.json +1 -1
- package/dist/channel-sdk.d.ts +28 -0
- package/dist/channel-sdk.d.ts.map +1 -0
- package/dist/channel-sdk.js +28 -0
- package/dist/channel-sdk.js.map +1 -0
- package/dist/cli/commands/channels.d.ts.map +1 -1
- package/dist/cli/commands/channels.js +8 -8
- package/dist/cli/commands/channels.js.map +1 -1
- package/dist/cli/commands/connect.d.ts +8 -11
- package/dist/cli/commands/connect.d.ts.map +1 -1
- package/dist/cli/commands/connect.js +157 -17
- package/dist/cli/commands/connect.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +64 -0
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/extensions.d.ts +46 -0
- package/dist/cli/commands/extensions.d.ts.map +1 -0
- package/dist/cli/commands/extensions.js +578 -0
- package/dist/cli/commands/extensions.js.map +1 -0
- package/dist/cli/commands/pairing.d.ts.map +1 -1
- package/dist/cli/commands/pairing.js +16 -2
- package/dist/cli/commands/pairing.js.map +1 -1
- package/dist/cli/commands/update.d.ts +17 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/update.js +104 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/program/build-program.d.ts.map +1 -1
- package/dist/cli/program/build-program.js +113 -0
- package/dist/cli/program/build-program.js.map +1 -1
- package/dist/config/paths.d.ts +1 -0
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +9 -0
- package/dist/config/paths.js.map +1 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +134 -2
- package/dist/core/server.js.map +1 -1
- package/dist/protocol.d.ts +25 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js.map +1 -1
- package/dist/system-prompt/assembler.d.ts.map +1 -1
- package/dist/system-prompt/assembler.js +17 -0
- package/dist/system-prompt/assembler.js.map +1 -1
- package/dist/system-prompt/identity-defaults.d.ts +28 -0
- package/dist/system-prompt/identity-defaults.d.ts.map +1 -1
- package/dist/system-prompt/identity-defaults.js +47 -0
- package/dist/system-prompt/identity-defaults.js.map +1 -1
- package/dist/ui/editor.d.ts.map +1 -1
- package/dist/ui/editor.js +1 -0
- package/dist/ui/editor.js.map +1 -1
- package/dist/version.d.ts +4 -3
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +27 -5
- package/dist/version.js.map +1 -1
- package/package.json +21 -4
- package/scripts/build-done.mjs +11 -2
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in channel metas — the single source of truth for the bundled
|
|
3
|
+
* channels' user-facing metadata (`ChannelMeta`).
|
|
4
|
+
*
|
|
5
|
+
* This module is DELIBERATELY import-light: it pulls in nothing but the
|
|
6
|
+
* `ChannelMeta` type, so the channel-meta registry (and through it, the
|
|
7
|
+
* system-prompt markdown gate + the exposure resolver) can read bundled
|
|
8
|
+
* channel metadata WITHOUT eagerly loading the channel adapters (Baileys
|
|
9
|
+
* sockets, the Telegram bot runtime). The plugins import THESE constants for
|
|
10
|
+
* their `meta` field, so there is exactly one definition per channel.
|
|
11
|
+
*
|
|
12
|
+
* Canonical channel ids are inlined as literals here — they are stable
|
|
13
|
+
* constants (`"whatsapp"`, `"telegram"`) and re-importing them from each
|
|
14
|
+
* channel's `account-config` module would risk dragging adapter-adjacent code
|
|
15
|
+
* into this light module. The plugin's own `WHATSAPP_CHANNEL_ID` /
|
|
16
|
+
* `TELEGRAM_CHANNEL_ID` literals must stay in sync with the `id` fields below;
|
|
17
|
+
* they are the same string by construction.
|
|
18
|
+
*/
|
|
19
|
+
/** WhatsApp channel metadata (markdown-capable; visible everywhere by default). */
|
|
20
|
+
export const WHATSAPP_CHANNEL_META = {
|
|
21
|
+
id: "whatsapp",
|
|
22
|
+
label: "WhatsApp",
|
|
23
|
+
selectionLabel: "WhatsApp",
|
|
24
|
+
docsPath: "channels/whatsapp",
|
|
25
|
+
blurb: "QR-pair a phone, DM/group chat over WhatsApp Web.",
|
|
26
|
+
order: 10,
|
|
27
|
+
markdownCapable: true,
|
|
28
|
+
};
|
|
29
|
+
/** Telegram channel metadata (markdown-capable; visible everywhere by default). */
|
|
30
|
+
export const TELEGRAM_CHANNEL_META = {
|
|
31
|
+
id: "telegram",
|
|
32
|
+
label: "Telegram",
|
|
33
|
+
selectionLabel: "Telegram",
|
|
34
|
+
docsPath: "channels/telegram",
|
|
35
|
+
blurb: "Paste a @BotFather token, DM/group chat over a Telegram bot.",
|
|
36
|
+
order: 20,
|
|
37
|
+
markdownCapable: true,
|
|
38
|
+
};
|
|
39
|
+
/** Every bundled channel meta, in declaration order. The registry seeds from this. */
|
|
40
|
+
export const BUNDLED_CHANNEL_METAS = [
|
|
41
|
+
WHATSAPP_CHANNEL_META,
|
|
42
|
+
TELEGRAM_CHANNEL_META,
|
|
43
|
+
];
|
|
44
|
+
//# sourceMappingURL=bundled-channel-metas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled-channel-metas.js","sourceRoot":"","sources":["../../../src/agents/channels/bundled-channel-metas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,mFAAmF;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAgB;IACjD,EAAE,EAAE,UAAU;IACd,KAAK,EAAE,UAAU;IACjB,cAAc,EAAE,UAAU;IAC1B,QAAQ,EAAE,mBAAmB;IAC7B,KAAK,EAAE,mDAAmD;IAC1D,KAAK,EAAE,EAAE;IACT,eAAe,EAAE,IAAI;CACrB,CAAC;AAEF,mFAAmF;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAgB;IACjD,EAAE,EAAE,UAAU;IACd,KAAK,EAAE,UAAU;IACjB,cAAc,EAAE,UAAU;IAC1B,QAAQ,EAAE,mBAAmB;IAC7B,KAAK,EAAE,8DAA8D;IACrE,KAAK,EAAE,EAAE;IACT,eAAe,EAAE,IAAI;CACrB,CAAC;AAEF,sFAAsF;AACtF,MAAM,CAAC,MAAM,qBAAqB,GAA2B;IAC5D,qBAAqB;IACrB,qBAAqB;CACrB,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-messaging registry — the process-wide lookup behind "how do I
|
|
3
|
+
* canonicalise / resolve an OUTBOUND target for channel <id>?".
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the `channel-meta-registry.ts` pattern (FIX #9): a dynamic
|
|
6
|
+
* registration seam keyed by lowercased channel id, resolved through one
|
|
7
|
+
* process-global singleton so a hot reload (or CLI + gateway in one process)
|
|
8
|
+
* shares a single slot.
|
|
9
|
+
*
|
|
10
|
+
* WHY A REGISTRY (and not "reach into the plugin off the manager"): the
|
|
11
|
+
* `send_message` tool runs against the LEGACY `ChannelManager`
|
|
12
|
+
* (`agents/channels/manager.ts`), which hands back a runtime `ChannelAdapter`
|
|
13
|
+
* via `adapter(id)` and deliberately exposes NO `ChannelPlugin` (the plugins
|
|
14
|
+
* live behind the multi-account plugin manager + `core/server.ts`). Pulling a
|
|
15
|
+
* channel plugin module into the send path would also eagerly load its adapter
|
|
16
|
+
* (Baileys sockets, the Telegram bot runtime). So instead the plugin engine
|
|
17
|
+
* registers JUST the plugin's `messaging` adapter here — plain function
|
|
18
|
+
* references, import-light — and the send tool consults this registry.
|
|
19
|
+
*
|
|
20
|
+
* A channel with NO `messaging` adapter simply never registers; the send tool's
|
|
21
|
+
* `resolveOutboundTarget` then returns the raw `to` unchanged, so back-compat
|
|
22
|
+
* is preserved by construction.
|
|
23
|
+
*/
|
|
24
|
+
import type { ChannelMessagingAdapter } from "./types.adapters.js";
|
|
25
|
+
/**
|
|
26
|
+
* Register (or replace) a channel's OUTBOUND messaging adapter. The plugin
|
|
27
|
+
* engine calls this when a channel module that declares `plugin.messaging`
|
|
28
|
+
* registers, so the `send_message` tool can address by name/handle/explicit
|
|
29
|
+
* target. Last registration per id wins. No-ops on an empty/unusable id.
|
|
30
|
+
*/
|
|
31
|
+
export declare function registerChannelMessagingAdapter(channelId: string | null | undefined, adapter: ChannelMessagingAdapter): void;
|
|
32
|
+
/**
|
|
33
|
+
* Bulk-register every messaging adapter declared on a plugin list (skipping
|
|
34
|
+
* plugins that omit the slot). The gateway bootstrap calls this once with its
|
|
35
|
+
* `bundledChannelPlugins` — parallel to how it seeds the meta registry — so the
|
|
36
|
+
* send tool can address by name/handle for any channel that opts in. Plugins
|
|
37
|
+
* WITHOUT a `messaging` adapter are simply skipped, leaving raw-id passthrough.
|
|
38
|
+
*/
|
|
39
|
+
export declare function syncChannelMessagingAdaptersFromPlugins(plugins: ReadonlyArray<{
|
|
40
|
+
id: string;
|
|
41
|
+
messaging?: ChannelMessagingAdapter;
|
|
42
|
+
}>): void;
|
|
43
|
+
/**
|
|
44
|
+
* Drop every dynamically-registered messaging adapter. PUBLIC — the gateway's
|
|
45
|
+
* `stopExtensions()` calls this during a `system.reload` teardown so the
|
|
46
|
+
* registry starts clean and `startExtensions()` re-syncs ONLY the currently-
|
|
47
|
+
* loaded channels (the sync seam is `.set()`-only and never removes a slot, so
|
|
48
|
+
* without this an edited/removed channel's messaging adapter would leak across
|
|
49
|
+
* the reload and keep rewriting outbound targets). Idempotent.
|
|
50
|
+
*/
|
|
51
|
+
export declare function clearChannelMessagingRegistry(): void;
|
|
52
|
+
/** Test-only alias of {@link clearChannelMessagingRegistry}. Kept so existing tests don't break. */
|
|
53
|
+
export declare function resetChannelMessagingRegistryForTests(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Look up a channel's registered OUTBOUND messaging adapter by id (or alias the
|
|
56
|
+
* caller already normalized). Returns `undefined` when the channel registered
|
|
57
|
+
* none — the caller then falls back to raw-id behaviour. Case-insensitive.
|
|
58
|
+
*/
|
|
59
|
+
export declare function getChannelMessagingAdapter(channelId: string | null | undefined): ChannelMessagingAdapter | undefined;
|
|
60
|
+
/** Outcome of {@link resolveOutboundTarget}. */
|
|
61
|
+
export type ResolvedOutboundTarget = {
|
|
62
|
+
/**
|
|
63
|
+
* The concrete target id to hand `ChannelAdapter.sendText`. When no
|
|
64
|
+
* messaging adapter is registered (or it couldn't improve on the input),
|
|
65
|
+
* this is the caller's raw `to`, byte-for-byte.
|
|
66
|
+
*/
|
|
67
|
+
to: string;
|
|
68
|
+
/**
|
|
69
|
+
* A different channel id the explicit form re-targeted to (e.g. the agent
|
|
70
|
+
* passed `telegram:123` while sending through whatsapp). `undefined` when
|
|
71
|
+
* the target stays on the same channel. The caller decides whether to honor
|
|
72
|
+
* a cross-channel hop (owner gate) — this helper only surfaces it.
|
|
73
|
+
*/
|
|
74
|
+
channelId?: string;
|
|
75
|
+
/** Whether a messaging adapter actually participated (for logging/tests). */
|
|
76
|
+
usedAdapter: boolean;
|
|
77
|
+
/** Whether the optional `targetResolver` resolved a human name → id. */
|
|
78
|
+
resolvedByName: boolean;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Heuristic: does this `to` look like a human NAME / handle rather than an
|
|
82
|
+
* already-concrete conversation id? Concrete ids carry channel-shape markers
|
|
83
|
+
* (a `:` scheme, an `@` domain like `…@s.whatsapp.net`, a leading `+`, or are
|
|
84
|
+
* all-digits). A bare word / `@handle` is treated as a name worth resolving.
|
|
85
|
+
* Deliberately conservative — when unsure we DON'T treat it as a name, so we
|
|
86
|
+
* never send a resolver a value that's plainly an id.
|
|
87
|
+
*/
|
|
88
|
+
export declare function looksLikeContactName(to: string): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Turn the agent's loose `to` into a concrete outbound target for `channelId`,
|
|
91
|
+
* using that channel's registered `messaging` adapter when present. The order
|
|
92
|
+
* matches the `ChannelMessagingAdapter` contract:
|
|
93
|
+
*
|
|
94
|
+
* 1. No adapter registered → return the raw `to` UNCHANGED (back-compat).
|
|
95
|
+
* 2. `parseExplicitTarget(to)` — honor an explicit `scheme:value` / `@handle`
|
|
96
|
+
* form (and surface any cross-channel id it named).
|
|
97
|
+
* 3. If we're still holding a NAME (not an explicit target) and the adapter
|
|
98
|
+
* ships a `targetResolver`, resolve name → id; on a null/throw result fall
|
|
99
|
+
* back to the name as-is.
|
|
100
|
+
* 4. `normalizeTarget(...)` — canonicalise the final target id.
|
|
101
|
+
*
|
|
102
|
+
* NEVER throws: a misbehaving adapter (parse/normalize/resolve throwing) is
|
|
103
|
+
* caught and degrades to the raw `to`, so a buggy channel can't break sends.
|
|
104
|
+
*/
|
|
105
|
+
export declare function resolveOutboundTarget(params: {
|
|
106
|
+
channelId: string;
|
|
107
|
+
to: string;
|
|
108
|
+
}): Promise<ResolvedOutboundTarget>;
|
|
109
|
+
/**
|
|
110
|
+
* Canonicalise an INCOMING peer id to a stable conversation/session identity
|
|
111
|
+
* using the channel's registered `messaging` adapter when it ships the optional
|
|
112
|
+
* `resolveInboundConversation` hook (the inverse of the outbound `targetResolver`).
|
|
113
|
+
* The inbound pipeline calls this just before the 8-tier route resolver so a
|
|
114
|
+
* name-addressed inbound collapses onto the SAME conversation/session the
|
|
115
|
+
* outbound side targets.
|
|
116
|
+
*
|
|
117
|
+
* Contract — mirrors {@link resolveOutboundTarget}, conservative by construction:
|
|
118
|
+
* 1. No messaging adapter registered, OR it has no `resolveInboundConversation`
|
|
119
|
+
* → return the raw `peerId` UNCHANGED.
|
|
120
|
+
* 2. The hook returns `null`/empty, OR throws → return the raw `peerId`.
|
|
121
|
+
* 3. Otherwise → return the canonicalised id.
|
|
122
|
+
*
|
|
123
|
+
* NEVER throws. When the result equals the raw `peerId` (the default for any
|
|
124
|
+
* channel that doesn't opt in), downstream routing is byte-identical to today.
|
|
125
|
+
*/
|
|
126
|
+
export declare function resolveInboundConversation(params: {
|
|
127
|
+
channelId: string;
|
|
128
|
+
peerId: string;
|
|
129
|
+
}): string;
|
|
130
|
+
//# sourceMappingURL=channel-messaging-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-messaging-registry.d.ts","sourceRoot":"","sources":["../../../src/agents/channels/channel-messaging-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAkBnE;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC9C,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACpC,OAAO,EAAE,uBAAuB,GAC9B,IAAI,CAIN;AAED;;;;;;GAMG;AACH,wBAAgB,uCAAuC,CACtD,OAAO,EAAE,aAAa,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,uBAAuB,CAAA;CAAE,CAAC,GACzE,IAAI,CAIN;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD;AAED,oGAAoG;AACpG,wBAAgB,qCAAqC,IAAI,IAAI,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACzC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAClC,uBAAuB,GAAG,SAAS,CAIrC;AAMD,gDAAgD;AAChD,MAAM,MAAM,sBAAsB,GAAG;IACpC;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,WAAW,EAAE,OAAO,CAAC;IACrB,wEAAwE;IACxE,cAAc,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAWxD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;CACX,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAgDlC;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAiBT"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-messaging registry — the process-wide lookup behind "how do I
|
|
3
|
+
* canonicalise / resolve an OUTBOUND target for channel <id>?".
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the `channel-meta-registry.ts` pattern (FIX #9): a dynamic
|
|
6
|
+
* registration seam keyed by lowercased channel id, resolved through one
|
|
7
|
+
* process-global singleton so a hot reload (or CLI + gateway in one process)
|
|
8
|
+
* shares a single slot.
|
|
9
|
+
*
|
|
10
|
+
* WHY A REGISTRY (and not "reach into the plugin off the manager"): the
|
|
11
|
+
* `send_message` tool runs against the LEGACY `ChannelManager`
|
|
12
|
+
* (`agents/channels/manager.ts`), which hands back a runtime `ChannelAdapter`
|
|
13
|
+
* via `adapter(id)` and deliberately exposes NO `ChannelPlugin` (the plugins
|
|
14
|
+
* live behind the multi-account plugin manager + `core/server.ts`). Pulling a
|
|
15
|
+
* channel plugin module into the send path would also eagerly load its adapter
|
|
16
|
+
* (Baileys sockets, the Telegram bot runtime). So instead the plugin engine
|
|
17
|
+
* registers JUST the plugin's `messaging` adapter here — plain function
|
|
18
|
+
* references, import-light — and the send tool consults this registry.
|
|
19
|
+
*
|
|
20
|
+
* A channel with NO `messaging` adapter simply never registers; the send tool's
|
|
21
|
+
* `resolveOutboundTarget` then returns the raw `to` unchanged, so back-compat
|
|
22
|
+
* is preserved by construction.
|
|
23
|
+
*/
|
|
24
|
+
import { resolveGlobalSingleton } from "../../shared/global-singleton.js";
|
|
25
|
+
import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js";
|
|
26
|
+
/** Process-global slot so a hot reload (or CLI+gateway in one process) shares one registry. */
|
|
27
|
+
const REGISTRY_STATE_KEY = Symbol.for("brigade.channelMessagingRegistry.state");
|
|
28
|
+
function createState() {
|
|
29
|
+
return { byChannelId: new Map() };
|
|
30
|
+
}
|
|
31
|
+
function getState() {
|
|
32
|
+
return resolveGlobalSingleton(REGISTRY_STATE_KEY, createState);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Register (or replace) a channel's OUTBOUND messaging adapter. The plugin
|
|
36
|
+
* engine calls this when a channel module that declares `plugin.messaging`
|
|
37
|
+
* registers, so the `send_message` tool can address by name/handle/explicit
|
|
38
|
+
* target. Last registration per id wins. No-ops on an empty/unusable id.
|
|
39
|
+
*/
|
|
40
|
+
export function registerChannelMessagingAdapter(channelId, adapter) {
|
|
41
|
+
const id = normalizeOptionalLowercaseString(channelId);
|
|
42
|
+
if (!id)
|
|
43
|
+
return;
|
|
44
|
+
getState().byChannelId.set(id, adapter);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Bulk-register every messaging adapter declared on a plugin list (skipping
|
|
48
|
+
* plugins that omit the slot). The gateway bootstrap calls this once with its
|
|
49
|
+
* `bundledChannelPlugins` — parallel to how it seeds the meta registry — so the
|
|
50
|
+
* send tool can address by name/handle for any channel that opts in. Plugins
|
|
51
|
+
* WITHOUT a `messaging` adapter are simply skipped, leaving raw-id passthrough.
|
|
52
|
+
*/
|
|
53
|
+
export function syncChannelMessagingAdaptersFromPlugins(plugins) {
|
|
54
|
+
for (const plugin of plugins) {
|
|
55
|
+
if (plugin.messaging)
|
|
56
|
+
registerChannelMessagingAdapter(plugin.id, plugin.messaging);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Drop every dynamically-registered messaging adapter. PUBLIC — the gateway's
|
|
61
|
+
* `stopExtensions()` calls this during a `system.reload` teardown so the
|
|
62
|
+
* registry starts clean and `startExtensions()` re-syncs ONLY the currently-
|
|
63
|
+
* loaded channels (the sync seam is `.set()`-only and never removes a slot, so
|
|
64
|
+
* without this an edited/removed channel's messaging adapter would leak across
|
|
65
|
+
* the reload and keep rewriting outbound targets). Idempotent.
|
|
66
|
+
*/
|
|
67
|
+
export function clearChannelMessagingRegistry() {
|
|
68
|
+
getState().byChannelId.clear();
|
|
69
|
+
}
|
|
70
|
+
/** Test-only alias of {@link clearChannelMessagingRegistry}. Kept so existing tests don't break. */
|
|
71
|
+
export function resetChannelMessagingRegistryForTests() {
|
|
72
|
+
clearChannelMessagingRegistry();
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Look up a channel's registered OUTBOUND messaging adapter by id (or alias the
|
|
76
|
+
* caller already normalized). Returns `undefined` when the channel registered
|
|
77
|
+
* none — the caller then falls back to raw-id behaviour. Case-insensitive.
|
|
78
|
+
*/
|
|
79
|
+
export function getChannelMessagingAdapter(channelId) {
|
|
80
|
+
const key = normalizeOptionalLowercaseString(channelId);
|
|
81
|
+
if (!key)
|
|
82
|
+
return undefined;
|
|
83
|
+
return getState().byChannelId.get(key);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Heuristic: does this `to` look like a human NAME / handle rather than an
|
|
87
|
+
* already-concrete conversation id? Concrete ids carry channel-shape markers
|
|
88
|
+
* (a `:` scheme, an `@` domain like `…@s.whatsapp.net`, a leading `+`, or are
|
|
89
|
+
* all-digits). A bare word / `@handle` is treated as a name worth resolving.
|
|
90
|
+
* Deliberately conservative — when unsure we DON'T treat it as a name, so we
|
|
91
|
+
* never send a resolver a value that's plainly an id.
|
|
92
|
+
*/
|
|
93
|
+
export function looksLikeContactName(to) {
|
|
94
|
+
const t = to.trim();
|
|
95
|
+
if (t.length === 0)
|
|
96
|
+
return false;
|
|
97
|
+
// `scheme:value` (telegram:123) — explicit id form, not a name.
|
|
98
|
+
if (/^[a-z][a-z0-9_-]*:/i.test(t))
|
|
99
|
+
return false;
|
|
100
|
+
// `user@domain` JID (14057144199@s.whatsapp.net) — an id, not a name.
|
|
101
|
+
if (/@[^@\s]+\.[^@\s]+$/.test(t))
|
|
102
|
+
return false;
|
|
103
|
+
// Phone-ish (+15551234567 / 15551234567) — an id, not a name.
|
|
104
|
+
if (/^\+?\d[\d\s().-]{4,}$/.test(t))
|
|
105
|
+
return false;
|
|
106
|
+
// `@handle` OR a plain word/words → treat as a name worth resolving.
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Turn the agent's loose `to` into a concrete outbound target for `channelId`,
|
|
111
|
+
* using that channel's registered `messaging` adapter when present. The order
|
|
112
|
+
* matches the `ChannelMessagingAdapter` contract:
|
|
113
|
+
*
|
|
114
|
+
* 1. No adapter registered → return the raw `to` UNCHANGED (back-compat).
|
|
115
|
+
* 2. `parseExplicitTarget(to)` — honor an explicit `scheme:value` / `@handle`
|
|
116
|
+
* form (and surface any cross-channel id it named).
|
|
117
|
+
* 3. If we're still holding a NAME (not an explicit target) and the adapter
|
|
118
|
+
* ships a `targetResolver`, resolve name → id; on a null/throw result fall
|
|
119
|
+
* back to the name as-is.
|
|
120
|
+
* 4. `normalizeTarget(...)` — canonicalise the final target id.
|
|
121
|
+
*
|
|
122
|
+
* NEVER throws: a misbehaving adapter (parse/normalize/resolve throwing) is
|
|
123
|
+
* caught and degrades to the raw `to`, so a buggy channel can't break sends.
|
|
124
|
+
*/
|
|
125
|
+
export async function resolveOutboundTarget(params) {
|
|
126
|
+
const { channelId, to } = params;
|
|
127
|
+
const adapter = getChannelMessagingAdapter(channelId);
|
|
128
|
+
// (1) No messaging adapter → raw-id passthrough, byte-for-byte.
|
|
129
|
+
if (!adapter) {
|
|
130
|
+
return { to, usedAdapter: false, resolvedByName: false };
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
let target = to;
|
|
134
|
+
let crossChannelId;
|
|
135
|
+
let isExplicit = false;
|
|
136
|
+
// (2) Explicit-target form?
|
|
137
|
+
const parsed = adapter.parseExplicitTarget(to);
|
|
138
|
+
if (parsed) {
|
|
139
|
+
isExplicit = true;
|
|
140
|
+
target = parsed.target;
|
|
141
|
+
if (parsed.channelId)
|
|
142
|
+
crossChannelId = parsed.channelId;
|
|
143
|
+
}
|
|
144
|
+
// (3) Name → id resolution (only when NOT an explicit target, the input
|
|
145
|
+
// reads like a name, and the channel ships a resolver).
|
|
146
|
+
let resolvedByName = false;
|
|
147
|
+
if (!isExplicit && typeof adapter.targetResolver === "function" && looksLikeContactName(target)) {
|
|
148
|
+
const resolved = await adapter.targetResolver(target);
|
|
149
|
+
if (resolved != null && resolved !== "") {
|
|
150
|
+
target = resolved;
|
|
151
|
+
resolvedByName = true;
|
|
152
|
+
}
|
|
153
|
+
// null/empty → fall through with the name unchanged (caller may still
|
|
154
|
+
// send to it verbatim, matching the no-resolver path).
|
|
155
|
+
}
|
|
156
|
+
// (4) Canonicalise.
|
|
157
|
+
const normalized = adapter.normalizeTarget(target);
|
|
158
|
+
const finalTo = normalized && normalized.length > 0 ? normalized : target;
|
|
159
|
+
return {
|
|
160
|
+
to: finalTo,
|
|
161
|
+
...(crossChannelId ? { channelId: crossChannelId } : {}),
|
|
162
|
+
usedAdapter: true,
|
|
163
|
+
resolvedByName,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// A misbehaving adapter must never break sends — degrade to the raw id.
|
|
168
|
+
return { to, usedAdapter: false, resolvedByName: false };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/* -------------------------------------------------------------------------
|
|
172
|
+
* Default inbound-conversation resolution (the inverse of resolveOutboundTarget)
|
|
173
|
+
* --------------------------------------------------------------------- */
|
|
174
|
+
/**
|
|
175
|
+
* Canonicalise an INCOMING peer id to a stable conversation/session identity
|
|
176
|
+
* using the channel's registered `messaging` adapter when it ships the optional
|
|
177
|
+
* `resolveInboundConversation` hook (the inverse of the outbound `targetResolver`).
|
|
178
|
+
* The inbound pipeline calls this just before the 8-tier route resolver so a
|
|
179
|
+
* name-addressed inbound collapses onto the SAME conversation/session the
|
|
180
|
+
* outbound side targets.
|
|
181
|
+
*
|
|
182
|
+
* Contract — mirrors {@link resolveOutboundTarget}, conservative by construction:
|
|
183
|
+
* 1. No messaging adapter registered, OR it has no `resolveInboundConversation`
|
|
184
|
+
* → return the raw `peerId` UNCHANGED.
|
|
185
|
+
* 2. The hook returns `null`/empty, OR throws → return the raw `peerId`.
|
|
186
|
+
* 3. Otherwise → return the canonicalised id.
|
|
187
|
+
*
|
|
188
|
+
* NEVER throws. When the result equals the raw `peerId` (the default for any
|
|
189
|
+
* channel that doesn't opt in), downstream routing is byte-identical to today.
|
|
190
|
+
*/
|
|
191
|
+
export function resolveInboundConversation(params) {
|
|
192
|
+
const { channelId, peerId } = params;
|
|
193
|
+
const adapter = getChannelMessagingAdapter(channelId);
|
|
194
|
+
// (1) No adapter / no inbound hook → raw peer id, byte-for-byte.
|
|
195
|
+
if (!adapter || typeof adapter.resolveInboundConversation !== "function") {
|
|
196
|
+
return peerId;
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
const resolved = adapter.resolveInboundConversation(peerId);
|
|
200
|
+
// (2) null / empty → keep the raw peer id (no behaviour change).
|
|
201
|
+
if (resolved == null || resolved === "")
|
|
202
|
+
return peerId;
|
|
203
|
+
// (3) canonicalised id.
|
|
204
|
+
return resolved;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// A misbehaving adapter must never break inbound routing — degrade.
|
|
208
|
+
return peerId;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=channel-messaging-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-messaging-registry.js","sourceRoot":"","sources":["../../../src/agents/channels/channel-messaging-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-meta registry — the process-wide lookup behind "what does channel
|
|
3
|
+
* <id> declare about itself?" (markdown capability, exposure, aliases, …).
|
|
4
|
+
*
|
|
5
|
+
* Brand-scrubbed analogue of upstream's `src/channels/registry.ts` +
|
|
6
|
+
* `src/channels/chat-meta.ts` two-tier lookup, collapsed to the slice Brigade
|
|
7
|
+
* needs: a built-in catalog of the bundled channels' metas, plus a dynamic
|
|
8
|
+
* registration seam for channels that register through the plugin engine.
|
|
9
|
+
*
|
|
10
|
+
* WHY A REGISTRY (and not "just read `bundledChannelPlugins`"): the gateway's
|
|
11
|
+
* `bundledChannelPlugins` list (`core/server.ts`) is only populated for the
|
|
12
|
+
* MULTI-ACCOUNT plugin path, and pulling a channel plugin module into the
|
|
13
|
+
* system-prompt layer would eagerly load its adapter (Baileys sockets, the
|
|
14
|
+
* Telegram bot runtime, …). This module is import-light on purpose: the
|
|
15
|
+
* built-in metas are plain data constants (see `bundled-channel-metas.ts`),
|
|
16
|
+
* so the markdown gate + exposure resolver can consult channel metadata from
|
|
17
|
+
* anywhere — including the system-prompt assembly — without dragging the
|
|
18
|
+
* runtime in.
|
|
19
|
+
*
|
|
20
|
+
* Lookup order (mirrors upstream's built-in-then-plugin precedence):
|
|
21
|
+
* 1. Dynamically-registered plugin metas (last registration wins per id).
|
|
22
|
+
* 2. The built-in bundled catalog (WhatsApp, Telegram).
|
|
23
|
+
*
|
|
24
|
+
* Ids and aliases are matched case-insensitively. Unknown ids return
|
|
25
|
+
* `undefined` so callers can apply their own default (the markdown gate
|
|
26
|
+
* deliberately defaults markdown ON for unknown channels — see
|
|
27
|
+
* `isMarkdownCapableChannel`).
|
|
28
|
+
*/
|
|
29
|
+
import type { ChannelMeta } from "./types.core.js";
|
|
30
|
+
/**
|
|
31
|
+
* Register (or replace) a channel's meta at runtime. The plugin engine calls
|
|
32
|
+
* this when a channel module registers, so external / future channels surface
|
|
33
|
+
* in the same lookups as the bundled ones. Last registration per id wins.
|
|
34
|
+
* No-ops on a meta without a usable id.
|
|
35
|
+
*/
|
|
36
|
+
export declare function registerChannelMeta(meta: ChannelMeta): void;
|
|
37
|
+
/**
|
|
38
|
+
* Drop every dynamically-registered meta (the built-in catalog is untouched).
|
|
39
|
+
* PUBLIC — the gateway's `stopExtensions()` calls this during a `system.reload`
|
|
40
|
+
* teardown so the registry starts clean and the next `startExtensions()` re-
|
|
41
|
+
* registers ONLY the currently-loaded channels (registration is `.set()`-only
|
|
42
|
+
* and never removes, so without this a removed channel's meta would leak across
|
|
43
|
+
* the reload). Idempotent.
|
|
44
|
+
*/
|
|
45
|
+
export declare function clearChannelMetaRegistry(): void;
|
|
46
|
+
/** Test-only alias of {@link clearChannelMetaRegistry}. Kept so existing tests don't break. */
|
|
47
|
+
export declare function resetChannelMetaRegistryForTests(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Look up a registered channel plugin's full `ChannelMeta` by id or alias.
|
|
50
|
+
* Returns `undefined` for an unknown channel. Case-insensitive.
|
|
51
|
+
*
|
|
52
|
+
* Named to mirror the upstream accessor (`getRegisteredChannelPluginMeta`) so
|
|
53
|
+
* the parity story is legible; `getChatChannelMeta` is the friendlier alias.
|
|
54
|
+
*/
|
|
55
|
+
export declare function getRegisteredChannelPluginMeta(channelId: string | null | undefined): ChannelMeta | undefined;
|
|
56
|
+
/** Friendlier alias for {@link getRegisteredChannelPluginMeta}. */
|
|
57
|
+
export declare function getChatChannelMeta(channelId: string | null | undefined): ChannelMeta | undefined;
|
|
58
|
+
/** Every channel meta currently known (built-in + dynamic), de-duplicated by id. */
|
|
59
|
+
export declare function listChannelMetas(): ChannelMeta[];
|
|
60
|
+
//# sourceMappingURL=channel-meta-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-meta-registry.d.ts","sourceRoot":"","sources":["../../../src/agents/channels/channel-meta-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAkBnD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAI3D;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,+FAA+F;AAC/F,wBAAgB,gCAAgC,IAAI,IAAI,CAEvD;AAyBD;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAI5G;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAEhG;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,IAAI,WAAW,EAAE,CAahD"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-meta registry — the process-wide lookup behind "what does channel
|
|
3
|
+
* <id> declare about itself?" (markdown capability, exposure, aliases, …).
|
|
4
|
+
*
|
|
5
|
+
* Brand-scrubbed analogue of upstream's `src/channels/registry.ts` +
|
|
6
|
+
* `src/channels/chat-meta.ts` two-tier lookup, collapsed to the slice Brigade
|
|
7
|
+
* needs: a built-in catalog of the bundled channels' metas, plus a dynamic
|
|
8
|
+
* registration seam for channels that register through the plugin engine.
|
|
9
|
+
*
|
|
10
|
+
* WHY A REGISTRY (and not "just read `bundledChannelPlugins`"): the gateway's
|
|
11
|
+
* `bundledChannelPlugins` list (`core/server.ts`) is only populated for the
|
|
12
|
+
* MULTI-ACCOUNT plugin path, and pulling a channel plugin module into the
|
|
13
|
+
* system-prompt layer would eagerly load its adapter (Baileys sockets, the
|
|
14
|
+
* Telegram bot runtime, …). This module is import-light on purpose: the
|
|
15
|
+
* built-in metas are plain data constants (see `bundled-channel-metas.ts`),
|
|
16
|
+
* so the markdown gate + exposure resolver can consult channel metadata from
|
|
17
|
+
* anywhere — including the system-prompt assembly — without dragging the
|
|
18
|
+
* runtime in.
|
|
19
|
+
*
|
|
20
|
+
* Lookup order (mirrors upstream's built-in-then-plugin precedence):
|
|
21
|
+
* 1. Dynamically-registered plugin metas (last registration wins per id).
|
|
22
|
+
* 2. The built-in bundled catalog (WhatsApp, Telegram).
|
|
23
|
+
*
|
|
24
|
+
* Ids and aliases are matched case-insensitively. Unknown ids return
|
|
25
|
+
* `undefined` so callers can apply their own default (the markdown gate
|
|
26
|
+
* deliberately defaults markdown ON for unknown channels — see
|
|
27
|
+
* `isMarkdownCapableChannel`).
|
|
28
|
+
*/
|
|
29
|
+
import { resolveGlobalSingleton } from "../../shared/global-singleton.js";
|
|
30
|
+
import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js";
|
|
31
|
+
import { BUNDLED_CHANNEL_METAS } from "./bundled-channel-metas.js";
|
|
32
|
+
/** Process-global slot so a hot reload (or CLI+gateway in one process) shares one registry. */
|
|
33
|
+
const REGISTRY_STATE_KEY = Symbol.for("brigade.channelMetaRegistry.state");
|
|
34
|
+
function createState() {
|
|
35
|
+
return { dynamic: new Map() };
|
|
36
|
+
}
|
|
37
|
+
function getState() {
|
|
38
|
+
return resolveGlobalSingleton(REGISTRY_STATE_KEY, createState);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Register (or replace) a channel's meta at runtime. The plugin engine calls
|
|
42
|
+
* this when a channel module registers, so external / future channels surface
|
|
43
|
+
* in the same lookups as the bundled ones. Last registration per id wins.
|
|
44
|
+
* No-ops on a meta without a usable id.
|
|
45
|
+
*/
|
|
46
|
+
export function registerChannelMeta(meta) {
|
|
47
|
+
const id = normalizeOptionalLowercaseString(meta?.id);
|
|
48
|
+
if (!id)
|
|
49
|
+
return;
|
|
50
|
+
getState().dynamic.set(id, meta);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Drop every dynamically-registered meta (the built-in catalog is untouched).
|
|
54
|
+
* PUBLIC — the gateway's `stopExtensions()` calls this during a `system.reload`
|
|
55
|
+
* teardown so the registry starts clean and the next `startExtensions()` re-
|
|
56
|
+
* registers ONLY the currently-loaded channels (registration is `.set()`-only
|
|
57
|
+
* and never removes, so without this a removed channel's meta would leak across
|
|
58
|
+
* the reload). Idempotent.
|
|
59
|
+
*/
|
|
60
|
+
export function clearChannelMetaRegistry() {
|
|
61
|
+
getState().dynamic.clear();
|
|
62
|
+
}
|
|
63
|
+
/** Test-only alias of {@link clearChannelMetaRegistry}. Kept so existing tests don't break. */
|
|
64
|
+
export function resetChannelMetaRegistryForTests() {
|
|
65
|
+
clearChannelMetaRegistry();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build the merged lookup map for a single resolution pass: built-in catalog
|
|
69
|
+
* first, then dynamic registrations layered on top (dynamic wins per id).
|
|
70
|
+
* Each meta is indexed by its own id AND every declared alias (all lowercased).
|
|
71
|
+
*/
|
|
72
|
+
function buildLookup() {
|
|
73
|
+
const byKey = new Map();
|
|
74
|
+
const index = (meta) => {
|
|
75
|
+
const id = normalizeOptionalLowercaseString(meta.id);
|
|
76
|
+
if (!id)
|
|
77
|
+
return;
|
|
78
|
+
byKey.set(id, meta);
|
|
79
|
+
for (const alias of meta.aliases ?? []) {
|
|
80
|
+
const a = normalizeOptionalLowercaseString(alias);
|
|
81
|
+
if (a)
|
|
82
|
+
byKey.set(a, meta);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
for (const meta of BUNDLED_CHANNEL_METAS)
|
|
86
|
+
index(meta);
|
|
87
|
+
// Dynamic registrations layer on top so an external plugin can override a
|
|
88
|
+
// bundled channel's meta (and so its id/aliases win on collision).
|
|
89
|
+
for (const meta of getState().dynamic.values())
|
|
90
|
+
index(meta);
|
|
91
|
+
return byKey;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Look up a registered channel plugin's full `ChannelMeta` by id or alias.
|
|
95
|
+
* Returns `undefined` for an unknown channel. Case-insensitive.
|
|
96
|
+
*
|
|
97
|
+
* Named to mirror the upstream accessor (`getRegisteredChannelPluginMeta`) so
|
|
98
|
+
* the parity story is legible; `getChatChannelMeta` is the friendlier alias.
|
|
99
|
+
*/
|
|
100
|
+
export function getRegisteredChannelPluginMeta(channelId) {
|
|
101
|
+
const key = normalizeOptionalLowercaseString(channelId);
|
|
102
|
+
if (!key)
|
|
103
|
+
return undefined;
|
|
104
|
+
return buildLookup().get(key);
|
|
105
|
+
}
|
|
106
|
+
/** Friendlier alias for {@link getRegisteredChannelPluginMeta}. */
|
|
107
|
+
export function getChatChannelMeta(channelId) {
|
|
108
|
+
return getRegisteredChannelPluginMeta(channelId);
|
|
109
|
+
}
|
|
110
|
+
/** Every channel meta currently known (built-in + dynamic), de-duplicated by id. */
|
|
111
|
+
export function listChannelMetas() {
|
|
112
|
+
const seen = new Set();
|
|
113
|
+
const out = [];
|
|
114
|
+
const push = (meta) => {
|
|
115
|
+
const id = normalizeOptionalLowercaseString(meta.id);
|
|
116
|
+
if (!id || seen.has(id))
|
|
117
|
+
return;
|
|
118
|
+
seen.add(id);
|
|
119
|
+
out.push(meta);
|
|
120
|
+
};
|
|
121
|
+
// Dynamic first so an override replaces the bundled entry in the output.
|
|
122
|
+
for (const meta of getState().dynamic.values())
|
|
123
|
+
push(meta);
|
|
124
|
+
for (const meta of BUNDLED_CHANNEL_METAS)
|
|
125
|
+
push(meta);
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=channel-meta-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-meta-registry.js","sourceRoot":"","sources":["../../../src/agents/channels/channel-meta-registry.ts"],"names":[],"mappings":"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"}
|