From 65404ceabbb5fb6e172cad5cb2e786694afa7080 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 2 May 2026 04:24:49 +0100 Subject: [PATCH] fix: avoid stale provider policy alias cache --- src/plugins/provider-public-artifacts.test.ts | 75 ++++++++++++++++++- src/plugins/provider-public-artifacts.ts | 9 --- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/plugins/provider-public-artifacts.test.ts b/src/plugins/provider-public-artifacts.test.ts index e54930e2a27..8779f14daaf 100644 --- a/src/plugins/provider-public-artifacts.test.ts +++ b/src/plugins/provider-public-artifacts.test.ts @@ -71,9 +71,13 @@ describe("provider public artifacts", () => { return { resolveThinkingProfile }; }); - vi.doMock("./bundled-dir.js", () => ({ - resolveBundledPluginsDir: () => bundledPluginsDir, - })); + vi.doMock("./bundled-dir.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + resolveBundledPluginsDir: () => bundledPluginsDir, + }; + }); process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = bundledPluginsDir; process.env.OPENCLAW_TEST_TRUST_BUNDLED_PLUGINS_DIR = "1"; vi.doMock("./public-surface-loader.js", () => ({ @@ -114,6 +118,71 @@ describe("provider public artifacts", () => { } }); + it("does not cache manifest-owned provider policy aliases across bundled metadata changes", async () => { + const bundledPluginsDir = fs.mkdtempSync( + path.join(os.tmpdir(), "openclaw-provider-policy-refresh-"), + ); + const writePlugin = (pluginId: string, providers: string[], version: number) => { + const pluginDir = path.join(bundledPluginsDir, pluginId); + fs.mkdirSync(pluginDir, { recursive: true }); + fs.writeFileSync( + path.join(pluginDir, "openclaw.plugin.json"), + JSON.stringify({ + id: pluginId, + name: `${pluginId} ${version}`, + configSchema: { type: "object" }, + providers, + }), + ); + fs.writeFileSync( + path.join(pluginDir, "index.js"), + "export default { register() {} };\n", + "utf8", + ); + }; + + const loadBundledPluginPublicArtifactModuleSync = vi.fn(({ dirName }: { dirName: string }) => { + if (dirName !== "first" && dirName !== "second") { + throw new Error(`Unable to resolve bundled plugin public surface ${dirName}`); + } + return { + resolveThinkingProfile: () => ({ levels: [{ id: dirName }] }), + }; + }); + + vi.doMock("./public-surface-loader.js", () => ({ + loadBundledPluginPublicArtifactModuleSync, + })); + process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = bundledPluginsDir; + process.env.OPENCLAW_TEST_TRUST_BUNDLED_PLUGINS_DIR = "1"; + vi.resetModules(); + + try { + writePlugin("first", ["fixture-provider"], 1); + writePlugin("second", [], 1); + const { resolveBundledProviderPolicySurface: resolvePolicySurface } = await importFreshModule< + typeof import("./provider-public-artifacts.js") + >(import.meta.url, "./provider-public-artifacts.js?scope=provider-alias-refresh"); + + expect( + resolvePolicySurface("fixture-provider") + ?.resolveThinkingProfile?.({ provider: "fixture-provider", modelId: "demo" }) + ?.levels.map((level) => level.id), + ).toEqual(["first"]); + + writePlugin("first", [], 2); + writePlugin("second", ["fixture-provider"], 2); + + expect( + resolvePolicySurface("fixture-provider") + ?.resolveThinkingProfile?.({ provider: "fixture-provider", modelId: "demo" }) + ?.levels.map((level) => level.id), + ).toEqual(["second"]); + } finally { + fs.rmSync(bundledPluginsDir, { force: true, recursive: true }); + } + }); + it("loads provider policy surfaces without package-manager repair", async () => { const loadBundledPluginPublicArtifactModuleSync = vi.fn(() => ({ normalizeConfig: (ctx: { providerConfig: ModelProviderConfig }) => ctx.providerConfig, diff --git a/src/plugins/provider-public-artifacts.ts b/src/plugins/provider-public-artifacts.ts index 1b81da19aa9..1b7c9998451 100644 --- a/src/plugins/provider-public-artifacts.ts +++ b/src/plugins/provider-public-artifacts.ts @@ -15,7 +15,6 @@ import type { import { loadBundledPluginPublicArtifactModuleSync } from "./public-surface-loader.js"; const PROVIDER_POLICY_ARTIFACT_CANDIDATES = ["provider-policy-api.js"] as const; -const providerPolicyPluginIdsByProviderId = new Map(); export type BundledProviderPolicySurface = { normalizeConfig?: (ctx: ProviderNormalizeConfigContext) => ModelProviderConfig | null | undefined; @@ -70,13 +69,7 @@ function resolveBundledProviderPolicyPluginId(providerId: string): string | null return null; } const bundledPluginsDir = resolveBundledPluginsDir(); - const cacheKey = `${bundledPluginsDir ?? ""}::${normalizedProviderId}`; - if (providerPolicyPluginIdsByProviderId.has(cacheKey)) { - return providerPolicyPluginIdsByProviderId.get(cacheKey) ?? null; - } - if (!bundledPluginsDir) { - providerPolicyPluginIdsByProviderId.set(cacheKey, null); return null; } @@ -91,12 +84,10 @@ function resolveBundledProviderPolicyPluginId(providerId: string): string | null (provider) => normalizeProviderId(provider) === normalizedProviderId, ); if (ownsProvider) { - providerPolicyPluginIdsByProviderId.set(cacheKey, plugin.id); return plugin.id; } } - providerPolicyPluginIdsByProviderId.set(cacheKey, null); return null; }