From b619d39e54bab157595164623899998ddc11283d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 22 Apr 2026 06:39:21 +0100 Subject: [PATCH] fix(channels): preserve setup promotion fallbacks --- CHANGELOG.md | 1 + .../plugins/setup-promotion-helpers.ts | 75 ++++++++++++------- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2361a9b2513..76d12f10fe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Channels/config: require resolved runtime config on channel send/action/client helpers and block runtime helper `loadConfig()` calls, so SecretRefs are resolved at startup/boundaries instead of being re-read during sends. +- CLI/channels: preserve bundled setup promotion metadata when a loaded partial channel plugin omits it, so adding a non-default account still moves legacy single-account fields such as Telegram `streaming` into `accounts.default`. - Telegram: keep the sent-message ownership cache isolated per configured session store, so own-message reaction filtering remains correct with custom `session.store` paths. - Ollama: forward OpenClaw thinking control to native `/api/chat` requests as top-level `think`, so `/think off` and `openclaw agent --thinking off` suppress thinking on models such as qwen3 instead of idling until the watchdog fires. Fixes #69902. (#69967) Thanks @WZH8898. - Memory-core/dreaming: suppress the startup-only managed dreaming cron unavailable warning when the cron service is still attaching, while preserving the runtime warning if cron genuinely remains unavailable. Fixes #69939. (#69941) Thanks @Sanjays2402. diff --git a/src/channels/plugins/setup-promotion-helpers.ts b/src/channels/plugins/setup-promotion-helpers.ts index 428371af66d..b38fd21261f 100644 --- a/src/channels/plugins/setup-promotion-helpers.ts +++ b/src/channels/plugins/setup-promotion-helpers.ts @@ -51,23 +51,23 @@ type ChannelSetupPromotionSurface = { const BUNDLED_CHANNELS_WITHOUT_SETUP_PROMOTION_SURFACE = new Set(["whatsapp"]); -function getChannelSetupPromotionSurface( +function asPromotionSurface(setup: unknown): ChannelSetupPromotionSurface | null { + return setup && typeof setup === "object" ? (setup as ChannelSetupPromotionSurface) : null; +} + +function getLoadedChannelSetupPromotionSurface( channelKey: string, - opts?: { loadBundledFallback?: boolean }, ): ChannelSetupPromotionSurface | null { - if ( - opts?.loadBundledFallback && - BUNDLED_CHANNELS_WITHOUT_SETUP_PROMOTION_SURFACE.has(channelKey) - ) { - return getLoadedChannelPlugin(channelKey)?.setup ?? null; - } - const setup = - getLoadedChannelPlugin(channelKey)?.setup ?? - (opts?.loadBundledFallback ? getBundledChannelPlugin(channelKey)?.setup : undefined); - if (!setup || typeof setup !== "object") { + return asPromotionSurface(getLoadedChannelPlugin(channelKey)?.setup); +} + +function getBundledChannelSetupPromotionSurface( + channelKey: string, +): ChannelSetupPromotionSurface | null { + if (BUNDLED_CHANNELS_WITHOUT_SETUP_PROMOTION_SURFACE.has(channelKey)) { return null; } - return setup as ChannelSetupPromotionSurface; + return asPromotionSurface(getBundledChannelPlugin(channelKey)?.setup); } function isStaticSingleAccountPromotionKey(key: string): boolean { @@ -81,10 +81,16 @@ export function shouldMoveSingleAccountChannelKey(params: { if (isStaticSingleAccountPromotionKey(params.key)) { return true; } - const contractKeys = getChannelSetupPromotionSurface(params.channelKey, { - loadBundledFallback: true, - })?.singleAccountKeysToMove; - if (contractKeys?.includes(params.key)) { + const loadedContractKeys = getLoadedChannelSetupPromotionSurface( + params.channelKey, + )?.singleAccountKeysToMove; + if (loadedContractKeys?.includes(params.key)) { + return true; + } + const bundledContractKeys = getBundledChannelSetupPromotionSurface( + params.channelKey, + )?.singleAccountKeysToMove; + if (bundledContractKeys?.includes(params.key)) { return true; } return false; @@ -104,26 +110,33 @@ export function resolveSingleAccountKeysToMove(params: { return []; } - let setupSurface: ChannelSetupPromotionSurface | null | undefined; - const resolveSetupSurface = () => { - setupSurface ??= getChannelSetupPromotionSurface(params.channelKey, { - loadBundledFallback: true, - }); - return setupSurface; + let loadedSetupSurface: ChannelSetupPromotionSurface | null | undefined; + const resolveLoadedSetupSurface = () => { + loadedSetupSurface ??= getLoadedChannelSetupPromotionSurface(params.channelKey); + return loadedSetupSurface; + }; + let bundledSetupSurface: ChannelSetupPromotionSurface | null | undefined; + const resolveBundledSetupSurface = () => { + bundledSetupSurface ??= getBundledChannelSetupPromotionSurface(params.channelKey); + return bundledSetupSurface; }; const keysToMove = entries.filter((key) => { if (isStaticSingleAccountPromotionKey(key)) { return true; } - return Boolean(resolveSetupSurface()?.singleAccountKeysToMove?.includes(key)); + return Boolean( + resolveLoadedSetupSurface()?.singleAccountKeysToMove?.includes(key) || + resolveBundledSetupSurface()?.singleAccountKeysToMove?.includes(key), + ); }); if (!hasNamedAccounts || keysToMove.length === 0) { return keysToMove; } const namedAccountPromotionKeys = - setupSurface?.namedAccountPromotionKeys ?? resolveSetupSurface()?.namedAccountPromotionKeys; + resolveLoadedSetupSurface()?.namedAccountPromotionKeys ?? + resolveBundledSetupSurface()?.namedAccountPromotionKeys; if (!namedAccountPromotionKeys) { return keysToMove; } @@ -142,10 +155,14 @@ export function resolveSingleAccountPromotionTarget(params: { ); return matchedAccountId ?? normalizedTargetAccountId; }; - const surface = getChannelSetupPromotionSurface(params.channelKey, { - loadBundledFallback: true, - }); - const resolved = surface?.resolveSingleAccountPromotionTarget?.({ + const loadedSurface = getLoadedChannelSetupPromotionSurface(params.channelKey); + const bundledSurface = loadedSurface?.resolveSingleAccountPromotionTarget + ? undefined + : getBundledChannelSetupPromotionSurface(params.channelKey); + const resolvePromotionTarget = + loadedSurface?.resolveSingleAccountPromotionTarget ?? + bundledSurface?.resolveSingleAccountPromotionTarget; + const resolved = resolvePromotionTarget?.({ channel: params.channel, }); const normalizedResolved = normalizeOptionalString(resolved);