fix(plugins): preserve gateway-bindable registry reuse

# Conflicts:
#	src/agents/runtime-plugins.test.ts
#	src/agents/runtime-plugins.ts
#	src/plugins/loader.ts
#	src/plugins/tools.ts
This commit is contained in:
Ayaan Zaidi 2026-03-29 09:53:30 +05:30
parent bb9394e123
commit e3faa99c6a
No known key found for this signature in database
4 changed files with 62 additions and 8 deletions

View File

@ -301,6 +301,7 @@ function resolvePluginLoadCacheContext(options: PluginLoadOptions = {}) {
includeSetupOnlyChannelPlugins,
preferSetupRuntimeForChannelPlugins,
shouldActivate: options.activate !== false,
runtimeSubagentMode: resolveRuntimeSubagentMode(options.runtimeOptions),
cacheKey,
};
}
@ -768,8 +769,12 @@ function warnAboutUntrackedLoadedPlugins(params: {
}
}
function activatePluginRegistry(registry: PluginRegistry, cacheKey: string): void {
setActivePluginRegistry(registry, cacheKey);
function activatePluginRegistry(
registry: PluginRegistry,
cacheKey: string,
runtimeSubagentMode: "default" | "explicit" | "gateway-bindable",
): void {
setActivePluginRegistry(registry, cacheKey, runtimeSubagentMode);
initializeGlobalHookRunner(registry);
}
@ -790,6 +795,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
preferSetupRuntimeForChannelPlugins,
shouldActivate,
cacheKey,
runtimeSubagentMode,
} = resolvePluginLoadCacheContext(options);
const logger = options.logger ?? defaultLogger();
const validateOnly = options.mode === "validate";
@ -805,7 +811,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
runtime: cached.memoryRuntime,
});
if (shouldActivate) {
activatePluginRegistry(cached.registry, cacheKey);
activatePluginRegistry(cached.registry, cacheKey, runtimeSubagentMode);
}
return cached.registry;
}
@ -1396,7 +1402,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
});
}
if (shouldActivate) {
activatePluginRegistry(registry, cacheKey);
activatePluginRegistry(registry, cacheKey, runtimeSubagentMode);
}
return registry;
}

View File

@ -15,6 +15,7 @@ type RegistryState = {
httpRoute: RegistrySurfaceState;
channel: RegistrySurfaceState;
key: string | null;
runtimeSubagentMode: "default" | "explicit" | "gateway-bindable";
};
const state: RegistryState = (() => {
@ -36,6 +37,7 @@ const state: RegistryState = (() => {
version: 0,
},
key: null,
runtimeSubagentMode: "default",
};
}
return globalState[REGISTRY_STATE];
@ -71,12 +73,17 @@ function syncTrackedSurface(
installSurfaceRegistry(surface, registry, false);
}
export function setActivePluginRegistry(registry: PluginRegistry, cacheKey?: string) {
export function setActivePluginRegistry(
registry: PluginRegistry,
cacheKey?: string,
runtimeSubagentMode: "default" | "explicit" | "gateway-bindable" = "default",
) {
state.activeRegistry = registry;
state.activeVersion += 1;
syncTrackedSurface(state.httpRoute, registry, true);
syncTrackedSurface(state.channel, registry, true);
state.key = cacheKey ?? null;
state.runtimeSubagentMode = runtimeSubagentMode;
}
export function getActivePluginRegistry(): PluginRegistry | null {
@ -175,6 +182,10 @@ export function getActivePluginRegistryKey(): string | null {
return state.key;
}
export function getActivePluginRuntimeSubagentMode(): "default" | "explicit" | "gateway-bindable" {
return state.runtimeSubagentMode;
}
export function getActivePluginRegistryVersion(): number {
return state.activeVersion;
}
@ -185,4 +196,5 @@ export function resetPluginRuntimeStateForTest(): void {
installSurfaceRegistry(state.httpRoute, null, false);
installSurfaceRegistry(state.channel, null, false);
state.key = null;
state.runtimeSubagentMode = "default";
}

View File

@ -208,6 +208,8 @@ describe("resolvePluginTools optional tools", () => {
({ resetPluginRuntimeStateForTest, setActivePluginRegistry } = await import("./runtime.js"));
resetPluginRuntimeStateForTest();
({ resolvePluginTools } = await import("./tools.js"));
({ resetPluginRuntimeStateForTest, setActivePluginRegistry } = await import("./runtime.js"));
resetPluginRuntimeStateForTest();
});
it("skips optional tools without explicit allowlist", () => {
@ -349,7 +351,7 @@ describe("resolvePluginTools optional tools", () => {
it("reuses the active registry for gateway-bindable tool loads before reloading", () => {
const activeRegistry = createOptionalDemoActiveRegistry();
setActivePluginRegistry(activeRegistry as never, "gateway-startup");
setActivePluginRegistry(activeRegistry as never, "gateway-startup", "gateway-bindable");
resolveRuntimePluginRegistryMock.mockReturnValue(undefined);
const tools = resolvePluginTools(
@ -381,4 +383,30 @@ describe("resolvePluginTools optional tools", () => {
},
});
});
it("reloads when gateway binding would otherwise reuse a default-mode active registry", () => {
setActivePluginRegistry(
{
tools: [],
diagnostics: [],
} as never,
"default-registry",
"default",
);
setOptionalDemoRegistry();
resolvePluginTools({
context: createContext() as never,
allowGatewaySubagentBinding: true,
toolAllowlist: ["optional_tool"],
});
expect(loadOpenClawPluginsMock).toHaveBeenCalledWith(
expect.objectContaining({
runtimeOptions: {
allowGatewaySubagentBinding: true,
},
}),
);
});
});

View File

@ -5,7 +5,11 @@ import { createSubsystemLogger } from "../logging/subsystem.js";
import { applyTestPluginDefaults, normalizePluginsConfig } from "./config-state.js";
import { resolveRuntimePluginRegistry, type PluginLoadOptions } from "./loader.js";
import { createPluginLoaderLogger } from "./logger.js";
import { getActivePluginRegistry } from "./runtime.js";
import {
getActivePluginRegistry,
getActivePluginRegistryKey,
getActivePluginRuntimeSubagentMode,
} from "./runtime.js";
import type { OpenClawPluginToolContext } from "./types.js";
const log = createSubsystemLogger("plugins");
@ -55,7 +59,11 @@ function resolvePluginToolRegistry(params: {
loadOptions: PluginLoadOptions;
allowGatewaySubagentBinding?: boolean;
}) {
if (params.allowGatewaySubagentBinding) {
if (
params.allowGatewaySubagentBinding &&
getActivePluginRegistryKey() &&
getActivePluginRuntimeSubagentMode() === "gateway-bindable"
) {
return getActivePluginRegistry() ?? resolveRuntimePluginRegistry(params.loadOptions);
}
return resolveRuntimePluginRegistry(params.loadOptions);