fix(plugins): reuse active registry during tool resolution (#52262)

Merged via squash.

Prepared head SHA: 55982a6be6
Co-authored-by: PerfectPan <24316656+PerfectPan@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
Nyx
2026-04-04 05:16:33 +08:00
committed by GitHub
parent 5b29483ab1
commit dc21e3bb1e
5 changed files with 192 additions and 25 deletions

View File

@@ -18,7 +18,6 @@ const mocks = vi.hoisted(() => ({
loadPluginManifestRegistry: vi.fn<() => MockManifestRegistry>(() =>
createEmptyMockManifestRegistry(),
),
withBundledPluginAllowlistCompat: vi.fn(({ config }) => config),
withBundledPluginEnablementCompat: vi.fn(({ config }) => config),
withBundledPluginVitestCompat: vi.fn(({ config }) => config),
}));
@@ -32,7 +31,6 @@ vi.mock("./manifest-registry.js", () => ({
}));
vi.mock("./bundled-compat.js", () => ({
withBundledPluginAllowlistCompat: mocks.withBundledPluginAllowlistCompat,
withBundledPluginEnablementCompat: mocks.withBundledPluginEnablementCompat,
withBundledPluginVitestCompat: mocks.withBundledPluginVitestCompat,
}));
@@ -49,10 +47,9 @@ function expectNoResolvedCapabilityProviders(providers: Array<{ id: string }>) {
function expectBundledCompatLoadPath(params: {
cfg: OpenClawConfig;
allowlistCompat: { plugins: { allow: string[] } };
enablementCompat: {
plugins: {
allow: string[];
allow?: string[];
entries: { openai: { enabled: boolean } };
};
};
@@ -61,12 +58,8 @@ function expectBundledCompatLoadPath(params: {
config: params.cfg,
env: process.env,
});
expect(mocks.withBundledPluginAllowlistCompat).toHaveBeenCalledWith({
config: params.cfg,
pluginIds: ["openai"],
});
expect(mocks.withBundledPluginEnablementCompat).toHaveBeenCalledWith({
config: params.allowlistCompat,
config: params.cfg,
pluginIds: ["openai"],
});
expect(mocks.withBundledPluginVitestCompat).toHaveBeenCalledWith({
@@ -81,14 +74,13 @@ function expectBundledCompatLoadPath(params: {
function createCompatChainConfig() {
const cfg = { plugins: { allow: ["custom-plugin"] } } as OpenClawConfig;
const allowlistCompat = { plugins: { allow: ["custom-plugin", "openai"] } };
const enablementCompat = {
plugins: {
allow: ["custom-plugin", "openai"],
allow: ["custom-plugin"],
entries: { openai: { enabled: true } },
},
};
return { cfg, allowlistCompat, enablementCompat };
return { cfg, enablementCompat };
}
function setBundledCapabilityFixture(contractKey: string) {
@@ -113,16 +105,14 @@ function expectCompatChainApplied(params: {
key: "speechProviders" | "mediaUnderstandingProviders" | "imageGenerationProviders";
contractKey: string;
cfg: OpenClawConfig;
allowlistCompat: { plugins: { allow: string[] } };
enablementCompat: {
plugins: {
allow: string[];
allow?: string[];
entries: { openai: { enabled: boolean } };
};
};
}) {
setBundledCapabilityFixture(params.contractKey);
mocks.withBundledPluginAllowlistCompat.mockReturnValue(params.allowlistCompat);
mocks.withBundledPluginEnablementCompat.mockReturnValue(params.enablementCompat);
mocks.withBundledPluginVitestCompat.mockReturnValue(params.enablementCompat);
expectNoResolvedCapabilityProviders(
@@ -141,8 +131,6 @@ describe("resolvePluginCapabilityProviders", () => {
mocks.resolveRuntimePluginRegistry.mockReturnValue(undefined);
mocks.loadPluginManifestRegistry.mockReset();
mocks.loadPluginManifestRegistry.mockReturnValue(createEmptyMockManifestRegistry());
mocks.withBundledPluginAllowlistCompat.mockReset();
mocks.withBundledPluginAllowlistCompat.mockImplementation(({ config }) => config);
mocks.withBundledPluginEnablementCompat.mockReset();
mocks.withBundledPluginEnablementCompat.mockImplementation(({ config }) => config);
mocks.withBundledPluginVitestCompat.mockReset();
@@ -216,16 +204,46 @@ describe("resolvePluginCapabilityProviders", () => {
["mediaUnderstandingProviders", "mediaUnderstandingProviders"],
["imageGenerationProviders", "imageGenerationProviders"],
] as const)("applies bundled compat before fallback loading for %s", (key, contractKey) => {
const { cfg, allowlistCompat, enablementCompat } = createCompatChainConfig();
const { cfg, enablementCompat } = createCompatChainConfig();
expectCompatChainApplied({
key,
contractKey,
cfg,
allowlistCompat,
enablementCompat,
});
});
it("does not re-add bundled capability plugins excluded by an explicit allowlist", () => {
const cfg = { plugins: { allow: ["custom-plugin"] } } as OpenClawConfig;
const enablementCompat = {
plugins: {
allow: ["custom-plugin"],
entries: { openai: { enabled: true } },
},
};
setBundledCapabilityFixture("speechProviders");
mocks.withBundledPluginEnablementCompat.mockReturnValue(enablementCompat);
mocks.withBundledPluginVitestCompat.mockReturnValue(enablementCompat);
expectNoResolvedCapabilityProviders(
resolvePluginCapabilityProviders({ key: "speechProviders", cfg }),
);
expect(mocks.withBundledPluginEnablementCompat).toHaveBeenCalledWith({
config: cfg,
pluginIds: ["openai"],
});
expect(mocks.withBundledPluginVitestCompat).toHaveBeenCalledWith({
config: enablementCompat,
pluginIds: ["openai"],
env: process.env,
});
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledWith({
config: enablementCompat,
});
});
it("reuses a compatible active registry even when the capability list is empty", () => {
const active = createEmptyPluginRegistry();
mocks.resolveRuntimePluginRegistry.mockReturnValue(active);