From e4199379ff2c354dad7dfc0586a61534fe124bc7 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 25 Apr 2026 18:52:56 -0700 Subject: [PATCH] fix(channels): resolve cold channel presence from registry --- src/channels/plugins/read-only.test.ts | 2 ++ src/channels/plugins/read-only.ts | 17 ++++++--- src/plugins/activation-planner.ts | 15 ++++---- src/plugins/channel-presence-policy.ts | 48 ++++++++++++++++++++------ 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/channels/plugins/read-only.test.ts b/src/channels/plugins/read-only.test.ts index 0871b9c4ef2..0a1cf2131ce 100644 --- a/src/channels/plugins/read-only.test.ts +++ b/src/channels/plugins/read-only.test.ts @@ -538,6 +538,7 @@ describe("listReadOnlyChannelPluginsForConfig", () => { { env: { ...process.env }, includePersistedAuthState: false, + includeSetupRuntimeFallback: true, }, ); @@ -715,6 +716,7 @@ describe("listReadOnlyChannelPluginsForConfig", () => { { env: { ...process.env }, includePersistedAuthState: false, + includeSetupRuntimeFallback: true, }, ); diff --git a/src/channels/plugins/read-only.ts b/src/channels/plugins/read-only.ts index 836dce85653..e2b0c58057d 100644 --- a/src/channels/plugins/read-only.ts +++ b/src/channels/plugins/read-only.ts @@ -7,10 +7,9 @@ import { resolveDiscoverableScopedChannelPluginIds, } from "../../plugins/channel-plugin-ids.js"; import { loadOpenClawPlugins } from "../../plugins/loader.js"; -import { - loadPluginManifestRegistry, - type PluginManifestRecord, -} from "../../plugins/manifest-registry.js"; +import { loadPluginManifestRegistryForInstalledIndex } from "../../plugins/manifest-registry-installed.js"; +import type { PluginManifestRecord } from "../../plugins/manifest-registry.js"; +import { loadPluginRegistrySnapshot } from "../../plugins/plugin-registry.js"; import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js"; import { sanitizeForLog } from "../../terminal/ansi.js"; import { getBundledChannelSetupPlugin } from "./bundled.js"; @@ -491,6 +490,7 @@ function resolveExternalReadOnlyChannelPluginIds(params: { workspaceDir: params.workspaceDir, env: params.env, cache: params.cache, + manifestRecords: params.records, }); if (candidatePluginIds.length === 0) { return []; @@ -521,11 +521,18 @@ export function resolveReadOnlyChannelPluginsForConfig( ): ReadOnlyChannelPluginResolution { const env = options.env ?? process.env; const workspaceDir = resolveReadOnlyWorkspaceDir(cfg, options); - const manifestRecords = loadPluginManifestRegistry({ + const pluginIndex = loadPluginRegistrySnapshot({ config: cfg, workspaceDir, env, cache: options.cache, + }); + const manifestRecords = loadPluginManifestRegistryForInstalledIndex({ + index: pluginIndex, + config: cfg, + workspaceDir, + env, + includeDisabled: true, }).plugins; const externalManifestRecords = listExternalChannelManifestRecords(manifestRecords); const configuredChannelIds = [ diff --git a/src/plugins/activation-planner.ts b/src/plugins/activation-planner.ts index f163d053754..2407f07195a 100644 --- a/src/plugins/activation-planner.ts +++ b/src/plugins/activation-planner.ts @@ -56,18 +56,21 @@ type ResolveManifestActivationPlanParams = { cache?: boolean; origin?: PluginOrigin; onlyPluginIds?: readonly string[]; + manifestRecords?: readonly PluginManifestRecord[]; }; export function resolveManifestActivationPlan( params: ResolveManifestActivationPlanParams, ): PluginActivationPlan { const onlyPluginIdSet = createPluginIdScopeSet(normalizePluginIdScope(params.onlyPluginIds)); - const registry = loadPluginManifestRegistry({ - config: params.config, - workspaceDir: params.workspaceDir, - env: params.env, - cache: params.cache, - }); + const registry = params.manifestRecords + ? { plugins: params.manifestRecords, diagnostics: [] } + : loadPluginManifestRegistry({ + config: params.config, + workspaceDir: params.workspaceDir, + env: params.env, + cache: params.cache, + }); const entries = registry.plugins .flatMap((plugin) => { if (params.origin && plugin.origin !== params.origin) { diff --git a/src/plugins/channel-presence-policy.ts b/src/plugins/channel-presence-policy.ts index f5a2008ac4d..14b9ef499ec 100644 --- a/src/plugins/channel-presence-policy.ts +++ b/src/plugins/channel-presence-policy.ts @@ -19,7 +19,9 @@ import { isBundledManifestOwner, passesManifestOwnerBasePolicy, } from "./manifest-owner-policy.js"; -import { loadPluginManifestRegistry, type PluginManifestRecord } from "./manifest-registry.js"; +import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js"; +import type { PluginManifestRecord } from "./manifest-registry.js"; +import { loadPluginRegistrySnapshot } from "./plugin-registry.js"; const IGNORED_CHANNEL_CONFIG_KEYS = new Set(["defaults", "modelByChannel"]); @@ -340,6 +342,27 @@ function listDisabledChannelIdsForConfig(config: OpenClawConfig): string[] { .filter((channelId): channelId is string => Boolean(channelId)); } +function loadInstalledChannelManifestRecords(params: { + config: OpenClawConfig; + workspaceDir?: string; + env: NodeJS.ProcessEnv; + cache?: boolean; +}): readonly PluginManifestRecord[] { + const index = loadPluginRegistrySnapshot({ + config: params.config, + workspaceDir: params.workspaceDir, + env: params.env, + cache: params.cache, + }); + return loadPluginManifestRegistryForInstalledIndex({ + index, + config: params.config, + workspaceDir: params.workspaceDir, + env: params.env, + includeDisabled: true, + }).plugins; +} + export function resolveConfiguredChannelPresencePolicy(params: { config: OpenClawConfig; activationSourceConfig?: OpenClawConfig; @@ -355,12 +378,12 @@ export function resolveConfiguredChannelPresencePolicy(params: { resolveAgentWorkspaceDir(params.config, resolveDefaultAgentId(params.config)); const records = params.manifestRecords ?? - loadPluginManifestRegistry({ + loadInstalledChannelManifestRecords({ config: params.config, workspaceDir, env, cache: params.cache, - }).plugins; + }); const disabledChannelIds = new Set(listDisabledChannelIdsForConfig(params.config)); const entrySources = new Map>(); @@ -471,17 +494,20 @@ function resolveScopedChannelOwnerPluginIds(params: { workspaceDir?: string; env: NodeJS.ProcessEnv; cache?: boolean; + manifestRecords?: readonly PluginManifestRecord[]; }): string[] { const channelIds = normalizeChannelIds(params.channelIds); if (channelIds.length === 0) { return []; } - const registry = loadPluginManifestRegistry({ - config: params.config, - workspaceDir: params.workspaceDir, - env: params.env, - cache: params.cache, - }); + const records = + params.manifestRecords ?? + loadInstalledChannelManifestRecords({ + config: params.config, + workspaceDir: params.workspaceDir, + env: params.env, + cache: params.cache, + }); const trustConfig = params.activationSourceConfig ?? params.config; const normalizedConfig = normalizePluginsConfig(trustConfig.plugins); const candidateIds = dedupeSortedPluginIds( @@ -495,6 +521,7 @@ function resolveScopedChannelOwnerPluginIds(params: { workspaceDir: params.workspaceDir, env: params.env, cache: params.cache, + manifestRecords: records, }); }), ); @@ -502,7 +529,7 @@ function resolveScopedChannelOwnerPluginIds(params: { return []; } const candidateIdSet = new Set(candidateIds); - return registry.plugins + return records .filter((plugin) => { if (!candidateIdSet.has(plugin.id)) { return false; @@ -525,6 +552,7 @@ export function resolveDiscoverableScopedChannelPluginIds(params: { workspaceDir?: string; env: NodeJS.ProcessEnv; cache?: boolean; + manifestRecords?: readonly PluginManifestRecord[]; }): string[] { return resolveScopedChannelOwnerPluginIds(params); }