From 898d6840dc32db3082ac1e541d6c3361f148b4fe Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 16 Mar 2026 02:20:09 -0700 Subject: [PATCH] Runtime: lazy-load Telegram and Slack channel ops --- src/plugins/runtime/runtime-channel.ts | 48 +++++-- .../runtime/runtime-slack-ops.runtime.ts | 10 ++ src/plugins/runtime/runtime-slack.ts | 83 ++++++++--- .../runtime/runtime-telegram-ops.runtime.ts | 18 +++ src/plugins/runtime/runtime-telegram.ts | 129 +++++++++++++----- 5 files changed, 231 insertions(+), 57 deletions(-) create mode 100644 src/plugins/runtime/runtime-slack-ops.runtime.ts create mode 100644 src/plugins/runtime/runtime-telegram-ops.runtime.ts diff --git a/src/plugins/runtime/runtime-channel.ts b/src/plugins/runtime/runtime-channel.ts index 23b47d48eeb..80bb1aba736 100644 --- a/src/plugins/runtime/runtime-channel.ts +++ b/src/plugins/runtime/runtime-channel.ts @@ -84,8 +84,28 @@ import { createRuntimeTelegram } from "./runtime-telegram.js"; import { createRuntimeWhatsApp } from "./runtime-whatsapp.js"; import type { PluginRuntime } from "./types.js"; +function defineCachedValue( + target: T, + key: K, + create: () => unknown, +): void { + let cached: unknown; + let ready = false; + Object.defineProperty(target, key, { + configurable: true, + enumerable: true, + get() { + if (!ready) { + cached = create(); + ready = true; + } + return cached; + }, + }); +} + export function createRuntimeChannel(): PluginRuntime["channel"] { - return { + const channelRuntime = { text: { chunkByNewline, chunkMarkdownText, @@ -167,12 +187,6 @@ export function createRuntimeChannel(): PluginRuntime["channel"] { shouldComputeCommandAuthorized, shouldHandleTextCommands, }, - discord: createRuntimeDiscord(), - slack: createRuntimeSlack(), - telegram: createRuntimeTelegram(), - signal: createRuntimeSignal(), - imessage: createRuntimeIMessage(), - whatsapp: createRuntimeWhatsApp(), line: { listLineAccountIds, resolveDefaultLineAccountId, @@ -190,5 +204,23 @@ export function createRuntimeChannel(): PluginRuntime["channel"] { buildTemplateMessageFromPayload, monitorLineProvider, }, - }; + } satisfies Omit< + PluginRuntime["channel"], + "discord" | "slack" | "telegram" | "signal" | "imessage" | "whatsapp" + > & + Partial< + Pick< + PluginRuntime["channel"], + "discord" | "slack" | "telegram" | "signal" | "imessage" | "whatsapp" + > + >; + + defineCachedValue(channelRuntime, "discord", createRuntimeDiscord); + defineCachedValue(channelRuntime, "slack", createRuntimeSlack); + defineCachedValue(channelRuntime, "telegram", createRuntimeTelegram); + defineCachedValue(channelRuntime, "signal", createRuntimeSignal); + defineCachedValue(channelRuntime, "imessage", createRuntimeIMessage); + defineCachedValue(channelRuntime, "whatsapp", createRuntimeWhatsApp); + + return channelRuntime as PluginRuntime["channel"]; } diff --git a/src/plugins/runtime/runtime-slack-ops.runtime.ts b/src/plugins/runtime/runtime-slack-ops.runtime.ts new file mode 100644 index 00000000000..e22662c3b7f --- /dev/null +++ b/src/plugins/runtime/runtime-slack-ops.runtime.ts @@ -0,0 +1,10 @@ +export { + listSlackDirectoryGroupsLive, + listSlackDirectoryPeersLive, +} from "../../../extensions/slack/src/directory-live.js"; +export { monitorSlackProvider } from "../../../extensions/slack/src/index.js"; +export { probeSlack } from "../../../extensions/slack/src/probe.js"; +export { resolveSlackChannelAllowlist } from "../../../extensions/slack/src/resolve-channels.js"; +export { resolveSlackUserAllowlist } from "../../../extensions/slack/src/resolve-users.js"; +export { sendMessageSlack } from "../../../extensions/slack/src/send.js"; +export { handleSlackAction } from "../../agents/tools/slack-actions.js"; diff --git a/src/plugins/runtime/runtime-slack.ts b/src/plugins/runtime/runtime-slack.ts index 095b14ec9c7..9579aed4c1b 100644 --- a/src/plugins/runtime/runtime-slack.ts +++ b/src/plugins/runtime/runtime-slack.ts @@ -1,24 +1,71 @@ -import { - listSlackDirectoryGroupsLive, - listSlackDirectoryPeersLive, -} from "../../../extensions/slack/src/directory-live.js"; -import { monitorSlackProvider } from "../../../extensions/slack/src/index.js"; -import { probeSlack } from "../../../extensions/slack/src/probe.js"; -import { resolveSlackChannelAllowlist } from "../../../extensions/slack/src/resolve-channels.js"; -import { resolveSlackUserAllowlist } from "../../../extensions/slack/src/resolve-users.js"; -import { sendMessageSlack } from "../../../extensions/slack/src/send.js"; -import { handleSlackAction } from "../../agents/tools/slack-actions.js"; import type { PluginRuntimeChannel } from "./types-channel.js"; +let runtimeSlackOpsPromise: Promise | null = null; + +function loadRuntimeSlackOps() { + runtimeSlackOpsPromise ??= import("./runtime-slack-ops.runtime.js"); + return runtimeSlackOpsPromise; +} + +const listDirectoryGroupsLiveLazy: PluginRuntimeChannel["slack"]["listDirectoryGroupsLive"] = + async (...args) => { + const { listSlackDirectoryGroupsLive } = await loadRuntimeSlackOps(); + return listSlackDirectoryGroupsLive(...args); + }; + +const listDirectoryPeersLiveLazy: PluginRuntimeChannel["slack"]["listDirectoryPeersLive"] = async ( + ...args +) => { + const { listSlackDirectoryPeersLive } = await loadRuntimeSlackOps(); + return listSlackDirectoryPeersLive(...args); +}; + +const probeSlackLazy: PluginRuntimeChannel["slack"]["probeSlack"] = async (...args) => { + const { probeSlack } = await loadRuntimeSlackOps(); + return probeSlack(...args); +}; + +const resolveChannelAllowlistLazy: PluginRuntimeChannel["slack"]["resolveChannelAllowlist"] = + async (...args) => { + const { resolveSlackChannelAllowlist } = await loadRuntimeSlackOps(); + return resolveSlackChannelAllowlist(...args); + }; + +const resolveUserAllowlistLazy: PluginRuntimeChannel["slack"]["resolveUserAllowlist"] = async ( + ...args +) => { + const { resolveSlackUserAllowlist } = await loadRuntimeSlackOps(); + return resolveSlackUserAllowlist(...args); +}; + +const sendMessageSlackLazy: PluginRuntimeChannel["slack"]["sendMessageSlack"] = async (...args) => { + const { sendMessageSlack } = await loadRuntimeSlackOps(); + return sendMessageSlack(...args); +}; + +const monitorSlackProviderLazy: PluginRuntimeChannel["slack"]["monitorSlackProvider"] = async ( + ...args +) => { + const { monitorSlackProvider } = await loadRuntimeSlackOps(); + return monitorSlackProvider(...args); +}; + +const handleSlackActionLazy: PluginRuntimeChannel["slack"]["handleSlackAction"] = async ( + ...args +) => { + const { handleSlackAction } = await loadRuntimeSlackOps(); + return handleSlackAction(...args); +}; + export function createRuntimeSlack(): PluginRuntimeChannel["slack"] { return { - listDirectoryGroupsLive: listSlackDirectoryGroupsLive, - listDirectoryPeersLive: listSlackDirectoryPeersLive, - probeSlack, - resolveChannelAllowlist: resolveSlackChannelAllowlist, - resolveUserAllowlist: resolveSlackUserAllowlist, - sendMessageSlack, - monitorSlackProvider, - handleSlackAction, + listDirectoryGroupsLive: listDirectoryGroupsLiveLazy, + listDirectoryPeersLive: listDirectoryPeersLiveLazy, + probeSlack: probeSlackLazy, + resolveChannelAllowlist: resolveChannelAllowlistLazy, + resolveUserAllowlist: resolveUserAllowlistLazy, + sendMessageSlack: sendMessageSlackLazy, + monitorSlackProvider: monitorSlackProviderLazy, + handleSlackAction: handleSlackActionLazy, }; } diff --git a/src/plugins/runtime/runtime-telegram-ops.runtime.ts b/src/plugins/runtime/runtime-telegram-ops.runtime.ts new file mode 100644 index 00000000000..dc463625b4f --- /dev/null +++ b/src/plugins/runtime/runtime-telegram-ops.runtime.ts @@ -0,0 +1,18 @@ +export { + auditTelegramGroupMembership, + collectTelegramUnmentionedGroupIds, +} from "../../../extensions/telegram/src/audit.js"; +export { monitorTelegramProvider } from "../../../extensions/telegram/src/monitor.js"; +export { probeTelegram } from "../../../extensions/telegram/src/probe.js"; +export { + deleteMessageTelegram, + editMessageReplyMarkupTelegram, + editMessageTelegram, + pinMessageTelegram, + renameForumTopicTelegram, + sendMessageTelegram, + sendPollTelegram, + sendTypingTelegram, + unpinMessageTelegram, +} from "../../../extensions/telegram/src/send.js"; +export { resolveTelegramToken } from "../../../extensions/telegram/src/token.js"; diff --git a/src/plugins/runtime/runtime-telegram.ts b/src/plugins/runtime/runtime-telegram.ts index 9481c718565..22061a7e00d 100644 --- a/src/plugins/runtime/runtime-telegram.ts +++ b/src/plugins/runtime/runtime-telegram.ts @@ -1,21 +1,5 @@ -import { - auditTelegramGroupMembership, - collectTelegramUnmentionedGroupIds, -} from "../../../extensions/telegram/src/audit.js"; +import { collectTelegramUnmentionedGroupIds } from "../../../extensions/telegram/src/audit.js"; import { telegramMessageActions } from "../../../extensions/telegram/src/channel-actions.js"; -import { monitorTelegramProvider } from "../../../extensions/telegram/src/monitor.js"; -import { probeTelegram } from "../../../extensions/telegram/src/probe.js"; -import { - deleteMessageTelegram, - editMessageReplyMarkupTelegram, - editMessageTelegram, - pinMessageTelegram, - renameForumTopicTelegram, - sendMessageTelegram, - sendPollTelegram, - sendTypingTelegram, - unpinMessageTelegram, -} from "../../../extensions/telegram/src/send.js"; import { setTelegramThreadBindingIdleTimeoutBySessionKey, setTelegramThreadBindingMaxAgeBySessionKey, @@ -24,22 +8,105 @@ import { resolveTelegramToken } from "../../../extensions/telegram/src/token.js" import { createTelegramTypingLease } from "./runtime-telegram-typing.js"; import type { PluginRuntimeChannel } from "./types-channel.js"; +let runtimeTelegramOpsPromise: Promise | null = + null; + +function loadRuntimeTelegramOps() { + runtimeTelegramOpsPromise ??= import("./runtime-telegram-ops.runtime.js"); + return runtimeTelegramOpsPromise; +} + +const auditGroupMembershipLazy: PluginRuntimeChannel["telegram"]["auditGroupMembership"] = async ( + ...args +) => { + const { auditTelegramGroupMembership } = await loadRuntimeTelegramOps(); + return auditTelegramGroupMembership(...args); +}; + +const probeTelegramLazy: PluginRuntimeChannel["telegram"]["probeTelegram"] = async (...args) => { + const { probeTelegram } = await loadRuntimeTelegramOps(); + return probeTelegram(...args); +}; + +const sendMessageTelegramLazy: PluginRuntimeChannel["telegram"]["sendMessageTelegram"] = async ( + ...args +) => { + const { sendMessageTelegram } = await loadRuntimeTelegramOps(); + return sendMessageTelegram(...args); +}; + +const sendPollTelegramLazy: PluginRuntimeChannel["telegram"]["sendPollTelegram"] = async ( + ...args +) => { + const { sendPollTelegram } = await loadRuntimeTelegramOps(); + return sendPollTelegram(...args); +}; + +const monitorTelegramProviderLazy: PluginRuntimeChannel["telegram"]["monitorTelegramProvider"] = + async (...args) => { + const { monitorTelegramProvider } = await loadRuntimeTelegramOps(); + return monitorTelegramProvider(...args); + }; + +const sendTypingTelegramLazy: PluginRuntimeChannel["telegram"]["typing"]["pulse"] = async ( + ...args +) => { + const { sendTypingTelegram } = await loadRuntimeTelegramOps(); + return sendTypingTelegram(...args); +}; + +const editMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["editMessage"] = + async (...args) => { + const { editMessageTelegram } = await loadRuntimeTelegramOps(); + return editMessageTelegram(...args); + }; + +const editMessageReplyMarkupTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["editReplyMarkup"] = + async (...args) => { + const { editMessageReplyMarkupTelegram } = await loadRuntimeTelegramOps(); + return editMessageReplyMarkupTelegram(...args); + }; + +const deleteMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["deleteMessage"] = + async (...args) => { + const { deleteMessageTelegram } = await loadRuntimeTelegramOps(); + return deleteMessageTelegram(...args); + }; + +const renameForumTopicTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["renameTopic"] = + async (...args) => { + const { renameForumTopicTelegram } = await loadRuntimeTelegramOps(); + return renameForumTopicTelegram(...args); + }; + +const pinMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["pinMessage"] = + async (...args) => { + const { pinMessageTelegram } = await loadRuntimeTelegramOps(); + return pinMessageTelegram(...args); + }; + +const unpinMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["unpinMessage"] = + async (...args) => { + const { unpinMessageTelegram } = await loadRuntimeTelegramOps(); + return unpinMessageTelegram(...args); + }; + export function createRuntimeTelegram(): PluginRuntimeChannel["telegram"] { return { - auditGroupMembership: auditTelegramGroupMembership, + auditGroupMembership: auditGroupMembershipLazy, collectUnmentionedGroupIds: collectTelegramUnmentionedGroupIds, - probeTelegram, + probeTelegram: probeTelegramLazy, resolveTelegramToken, - sendMessageTelegram, - sendPollTelegram, - monitorTelegramProvider, + sendMessageTelegram: sendMessageTelegramLazy, + sendPollTelegram: sendPollTelegramLazy, + monitorTelegramProvider: monitorTelegramProviderLazy, messageActions: telegramMessageActions, threadBindings: { setIdleTimeoutBySessionKey: setTelegramThreadBindingIdleTimeoutBySessionKey, setMaxAgeBySessionKey: setTelegramThreadBindingMaxAgeBySessionKey, }, typing: { - pulse: sendTypingTelegram, + pulse: sendTypingTelegramLazy, start: async ({ to, accountId, cfg, intervalMs, messageThreadId }) => await createTelegramTypingLease({ to, @@ -48,7 +115,7 @@ export function createRuntimeTelegram(): PluginRuntimeChannel["telegram"] { intervalMs, messageThreadId, pulse: async ({ to, accountId, cfg, messageThreadId }) => - await sendTypingTelegram(to, { + await sendTypingTelegramLazy(to, { accountId, cfg, messageThreadId, @@ -56,14 +123,14 @@ export function createRuntimeTelegram(): PluginRuntimeChannel["telegram"] { }), }, conversationActions: { - editMessage: editMessageTelegram, - editReplyMarkup: editMessageReplyMarkupTelegram, + editMessage: editMessageTelegramLazy, + editReplyMarkup: editMessageReplyMarkupTelegramLazy, clearReplyMarkup: async (chatIdInput, messageIdInput, opts = {}) => - await editMessageReplyMarkupTelegram(chatIdInput, messageIdInput, [], opts), - deleteMessage: deleteMessageTelegram, - renameTopic: renameForumTopicTelegram, - pinMessage: pinMessageTelegram, - unpinMessage: unpinMessageTelegram, + await editMessageReplyMarkupTelegramLazy(chatIdInput, messageIdInput, [], opts), + deleteMessage: deleteMessageTelegramLazy, + renameTopic: renameForumTopicTelegramLazy, + pinMessage: pinMessageTelegramLazy, + unpinMessage: unpinMessageTelegramLazy, }, }; }