@@ -37,7 +37,11 @@ import {
3737import { isPathInside , safeStatSync } from "./path-safety.js" ;
3838import { createPluginRegistry , type PluginRecord , type PluginRegistry } from "./registry.js" ;
3939import { resolvePluginCacheInputs } from "./roots.js" ;
40- import { setActivePluginRegistry } from "./runtime.js" ;
40+ import {
41+ getActivePluginRegistry ,
42+ getActivePluginRegistryKey ,
43+ setActivePluginRegistry ,
44+ } from "./runtime.js" ;
4145import type { CreatePluginRuntimeOptions } from "./runtime/index.js" ;
4246import type { PluginRuntime } from "./runtime/types.js" ;
4347import { validateJsonSchemaValue } from "./schema-validator.js" ;
@@ -239,6 +243,80 @@ function normalizeScopedPluginIds(ids?: string[]): string[] | undefined {
239243 return normalized . length > 0 ? normalized : undefined ;
240244}
241245
246+ function resolveRuntimeSubagentMode (
247+ runtimeOptions : PluginLoadOptions [ "runtimeOptions" ] ,
248+ ) : "default" | "explicit" | "gateway-bindable" {
249+ if ( runtimeOptions ?. allowGatewaySubagentBinding === true ) {
250+ return "gateway-bindable" ;
251+ }
252+ if ( runtimeOptions ?. subagent ) {
253+ return "explicit" ;
254+ }
255+ return "default" ;
256+ }
257+
258+ function hasExplicitCompatibilityInputs ( options : PluginLoadOptions ) : boolean {
259+ return Boolean (
260+ options . config !== undefined ||
261+ options . workspaceDir !== undefined ||
262+ options . env !== undefined ||
263+ options . onlyPluginIds ?. length ||
264+ options . runtimeOptions !== undefined ||
265+ options . pluginSdkResolution !== undefined ||
266+ options . includeSetupOnlyChannelPlugins === true ||
267+ options . preferSetupRuntimeForChannelPlugins === true ,
268+ ) ;
269+ }
270+
271+ export function resolvePluginLoadCacheContext ( options : PluginLoadOptions = { } ) {
272+ const env = options . env ?? process . env ;
273+ const cfg = applyTestPluginDefaults ( options . config ?? { } , env ) ;
274+ const normalized = normalizePluginsConfig ( cfg . plugins ) ;
275+ const onlyPluginIds = normalizeScopedPluginIds ( options . onlyPluginIds ) ;
276+ const includeSetupOnlyChannelPlugins = options . includeSetupOnlyChannelPlugins === true ;
277+ const preferSetupRuntimeForChannelPlugins = options . preferSetupRuntimeForChannelPlugins === true ;
278+ const cacheKey = buildCacheKey ( {
279+ workspaceDir : options . workspaceDir ,
280+ plugins : normalized ,
281+ installs : cfg . plugins ?. installs ,
282+ env,
283+ onlyPluginIds,
284+ includeSetupOnlyChannelPlugins,
285+ preferSetupRuntimeForChannelPlugins,
286+ runtimeSubagentMode : resolveRuntimeSubagentMode ( options . runtimeOptions ) ,
287+ pluginSdkResolution : options . pluginSdkResolution ,
288+ } ) ;
289+ return {
290+ env,
291+ cfg,
292+ normalized,
293+ onlyPluginIds,
294+ includeSetupOnlyChannelPlugins,
295+ preferSetupRuntimeForChannelPlugins,
296+ shouldActivate : options . activate !== false ,
297+ cacheKey,
298+ } ;
299+ }
300+
301+ export function getCompatibleActivePluginRegistry (
302+ options : PluginLoadOptions = { } ,
303+ ) : PluginRegistry | undefined {
304+ const activeRegistry = getActivePluginRegistry ( ) ?? undefined ;
305+ if ( ! activeRegistry ) {
306+ return undefined ;
307+ }
308+ if ( ! hasExplicitCompatibilityInputs ( options ) ) {
309+ return activeRegistry ;
310+ }
311+ const activeCacheKey = getActivePluginRegistryKey ( ) ;
312+ if ( ! activeCacheKey ) {
313+ return undefined ;
314+ }
315+ return resolvePluginLoadCacheContext ( options ) . cacheKey === activeCacheKey
316+ ? activeRegistry
317+ : undefined ;
318+ }
319+
242320function validatePluginConfig ( params : {
243321 schema ?: Record < string , unknown > ;
244322 cacheKey ?: string ;
@@ -687,38 +765,19 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
687765 "loadOpenClawPlugins: activate:false requires cache:false to prevent command registry divergence" ,
688766 ) ;
689767 }
690- const env = options . env ?? process . env ;
691- // Test env: default-disable plugins unless explicitly configured.
692- // This keeps unit/gateway suites fast and avoids loading heavyweight plugin deps by accident.
693- const cfg = applyTestPluginDefaults ( options . config ?? { } , env ) ;
694- const logger = options . logger ?? defaultLogger ( ) ;
695- const validateOnly = options . mode === "validate" ;
696- const normalized = normalizePluginsConfig ( cfg . plugins ) ;
697- const onlyPluginIds = normalizeScopedPluginIds ( options . onlyPluginIds ) ;
698- const onlyPluginIdSet = onlyPluginIds ? new Set ( onlyPluginIds ) : null ;
699- const includeSetupOnlyChannelPlugins = options . includeSetupOnlyChannelPlugins === true ;
700- const preferSetupRuntimeForChannelPlugins = options . preferSetupRuntimeForChannelPlugins === true ;
701- const shouldActivate = options . activate !== false ;
702- // NOTE: `activate` is intentionally excluded from the cache key. All non-activating
703- // (snapshot) callers pass `cache: false` via loadOnboardingPluginRegistry(), so they
704- // never read from or write to the cache. Including `activate` here would be misleading
705- // — it would imply mixed-activate caching is supported, when in practice it is not.
706- const cacheKey = buildCacheKey ( {
707- workspaceDir : options . workspaceDir ,
708- plugins : normalized ,
709- installs : cfg . plugins ?. installs ,
768+ const {
710769 env,
770+ cfg,
771+ normalized,
711772 onlyPluginIds,
712773 includeSetupOnlyChannelPlugins,
713774 preferSetupRuntimeForChannelPlugins,
714- runtimeSubagentMode :
715- options . runtimeOptions ?. allowGatewaySubagentBinding === true
716- ? "gateway-bindable"
717- : options . runtimeOptions ?. subagent
718- ? "explicit"
719- : "default" ,
720- pluginSdkResolution : options . pluginSdkResolution ,
721- } ) ;
775+ shouldActivate,
776+ cacheKey,
777+ } = resolvePluginLoadCacheContext ( options ) ;
778+ const logger = options . logger ?? defaultLogger ( ) ;
779+ const validateOnly = options . mode === "validate" ;
780+ const onlyPluginIdSet = onlyPluginIds ? new Set ( onlyPluginIds ) : null ;
722781 const cacheEnabled = options . cache !== false ;
723782 if ( cacheEnabled ) {
724783 const cached = getCachedPluginRegistry ( cacheKey ) ;
0 commit comments