Files
openclaw/src/plugins/runtime/standalone-runtime-registry-loader.ts
Edionwheels 66ffb29679 fix(plugins): cold-load partial tool registries
Fix plugin tool discovery when a selected wildcard plugin set is resolved against a partial active registry.\n\nRequire scoped registries to cover every requested plugin owner, force cold-load incomplete tool discovery registries without replacing active plugin runtime state, and add regression coverage for the partial-registry path.\n\nFixes #76780.\nThanks @lilesjtu.
2026-05-03 19:09:34 +01:00

101 lines
2.7 KiB
TypeScript

import {
type ActiveRuntimePluginRegistrySurface,
getLoadedRuntimePluginRegistry,
} from "../active-runtime-registry.js";
import {
loadOpenClawPlugins,
resolvePluginRegistryLoadCacheKey,
type PluginLoadOptions,
} from "../loader.js";
import type { PluginRegistry } from "../registry-types.js";
import {
pinActivePluginChannelRegistry,
pinActivePluginHttpRouteRegistry,
setActivePluginRegistry,
} from "../runtime.js";
function resolveRuntimeSubagentMode(
loadOptions: PluginLoadOptions,
): "default" | "explicit" | "gateway-bindable" {
if (loadOptions.runtimeOptions?.allowGatewaySubagentBinding === true) {
return "gateway-bindable";
}
if (loadOptions.runtimeOptions?.subagent) {
return "explicit";
}
return "default";
}
function installStandaloneRegistry(
registry: PluginRegistry,
params: {
loadOptions: PluginLoadOptions;
surface: ActiveRuntimePluginRegistrySurface;
},
): void {
const cacheKey = resolvePluginRegistryLoadCacheKey(params.loadOptions);
const mode = resolveRuntimeSubagentMode(params.loadOptions);
setActivePluginRegistry(registry, cacheKey, mode, params.loadOptions.workspaceDir);
switch (params.surface) {
case "active":
break;
case "channel":
pinActivePluginChannelRegistry(registry);
break;
case "http-route":
pinActivePluginHttpRouteRegistry(registry);
break;
}
}
export function ensureStandaloneRuntimePluginRegistryLoaded(params: {
loadOptions: PluginLoadOptions;
forceLoad?: boolean;
installRegistry?: boolean;
requiredPluginIds?: readonly string[];
surface?: ActiveRuntimePluginRegistrySurface;
}): PluginRegistry | undefined {
const requiredPluginIds = params.requiredPluginIds ?? params.loadOptions.onlyPluginIds;
const surface = params.surface ?? "active";
if (!params.forceLoad) {
const existing = getLoadedRuntimePluginRegistry({
env: params.loadOptions.env,
loadOptions: params.loadOptions,
workspaceDir: params.loadOptions.workspaceDir,
requiredPluginIds,
surface,
});
if (existing) {
return existing;
}
}
const effectiveLoadOptions = params.forceLoad
? { ...params.loadOptions, cache: false }
: params.loadOptions;
const registry = loadOpenClawPlugins(effectiveLoadOptions);
if (params.loadOptions.activate !== false) {
switch (surface) {
case "active":
break;
case "channel":
pinActivePluginChannelRegistry(registry);
break;
case "http-route":
pinActivePluginHttpRouteRegistry(registry);
break;
}
return registry;
}
if (params.installRegistry === false) {
return registry;
}
installStandaloneRegistry(registry, {
loadOptions: params.loadOptions,
surface,
});
return registry;
}