From 4373af034cd6da9791eb4bcfaba3fb0ebab7f07e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 27 May 2026 05:10:35 +0100 Subject: [PATCH] fix: preserve bundled provider allowlist migration --- .../doctor/shared/deprecation-compat.test.ts | 1 + .../doctor/shared/deprecation-compat.ts | 12 +++++++ .../shared/legacy-config-migrate.test.ts | 27 +++++++++++++++ ...acy-config-migrations.runtime.providers.ts | 34 +++++++++++++++++++ 4 files changed, 74 insertions(+) diff --git a/src/commands/doctor/shared/deprecation-compat.test.ts b/src/commands/doctor/shared/deprecation-compat.test.ts index 8b1092d639e..1a9bfd00527 100644 --- a/src/commands/doctor/shared/deprecation-compat.test.ts +++ b/src/commands/doctor/shared/deprecation-compat.test.ts @@ -14,6 +14,7 @@ const requiredDoctorCompatCodes = [ "doctor-agent-embedded-pi-config", "doctor-plugin-install-config-ledger", "doctor-bundled-plugin-load-paths", + "doctor-bundled-provider-discovery-allowlist", "doctor-message-queue-steering-modes", "doctor-web-search-plugin-config", "doctor-web-fetch-plugin-config", diff --git a/src/commands/doctor/shared/deprecation-compat.ts b/src/commands/doctor/shared/deprecation-compat.ts index 9ff170c0b98..ef79a505cee 100644 --- a/src/commands/doctor/shared/deprecation-compat.ts +++ b/src/commands/doctor/shared/deprecation-compat.ts @@ -240,6 +240,18 @@ const DOCTOR_DEPRECATION_COMPAT_RECORDS = [ docsPath: "/cli/plugins#registry", tests: ["src/commands/doctor/shared/bundled-plugin-load-paths.test.ts"], }), + deprecatedCompatRecord({ + code: "doctor-bundled-provider-discovery-allowlist", + owner: "plugin", + introduced: "2026-04-25", + source: "plugins.allow configs created before bundled provider discovery was explicit", + migration: "src/commands/doctor/shared/legacy-config-migrations.runtime.providers.ts", + replacement: "plugins.bundledDiscovery allowlist mode plus explicit plugin/provider entries", + docsPath: "/cli/plugins#registry", + tests: ["src/commands/doctor/shared/legacy-config-migrate.test.ts"], + notes: + "Doctor preserves the shipped upgrade path only; runtime compatibility should stay behind explicit bundledDiscovery config.", + }), deprecatedCompatRecord({ code: "doctor-web-search-plugin-config", owner: "provider", diff --git a/src/commands/doctor/shared/legacy-config-migrate.test.ts b/src/commands/doctor/shared/legacy-config-migrate.test.ts index 3c08eadb6ca..b33ea204414 100644 --- a/src/commands/doctor/shared/legacy-config-migrate.test.ts +++ b/src/commands/doctor/shared/legacy-config-migrate.test.ts @@ -1204,6 +1204,33 @@ describe("legacy migrate x_search auth", () => { }); }); +describe("legacy bundled provider discovery migrate", () => { + it("sets compat mode for existing restrictive plugin allowlists", () => { + const res = migrateLegacyConfigForTest({ + plugins: { + allow: ["telegram"], + }, + }); + + expect(res.config?.plugins?.bundledDiscovery).toBe("compat"); + expect(res.changes).toStrictEqual([ + 'Set plugins.bundledDiscovery="compat" to preserve legacy bundled provider discovery for this restrictive plugins.allow config.', + ]); + }); + + it("does not override explicit bundled discovery mode", () => { + const res = migrateLegacyConfigForTest({ + plugins: { + allow: ["telegram"], + bundledDiscovery: "allowlist", + }, + }); + + expect(res.config).toBeNull(); + expect(res.changes).toStrictEqual([]); + }); +}); + describe("legacy migrate heartbeat config", () => { it("moves top-level heartbeat into agents.defaults.heartbeat", () => { const res = migrateLegacyConfigForTest({ diff --git a/src/commands/doctor/shared/legacy-config-migrations.runtime.providers.ts b/src/commands/doctor/shared/legacy-config-migrations.runtime.providers.ts index 89e26d797e0..2724cfcb50f 100644 --- a/src/commands/doctor/shared/legacy-config-migrations.runtime.providers.ts +++ b/src/commands/doctor/shared/legacy-config-migrations.runtime.providers.ts @@ -3,8 +3,23 @@ import { type LegacyConfigMigrationSpec, type LegacyConfigRule, } from "../../../config/legacy.shared.js"; +import { isRecord } from "./legacy-config-record-shared.js"; import { migrateLegacyXSearchConfig } from "./legacy-x-search-migrate.js"; +const BUNDLED_DISCOVERY_COMPAT_RULE: LegacyConfigRule = { + path: ["plugins", "allow"], + message: + 'plugins.allow now gates bundled provider discovery by default; run "openclaw doctor --fix" to preserve legacy bundled provider compatibility as plugins.bundledDiscovery="compat", or set plugins.bundledDiscovery="allowlist" to keep the stricter behavior.', + requireSourceLiteral: true, + match: (value, root) => { + if (!Array.isArray(value) || value.length === 0) { + return false; + } + const plugins = isRecord(root.plugins) ? root.plugins : undefined; + return plugins?.bundledDiscovery === undefined; + }, +}; + const X_SEARCH_RULE: LegacyConfigRule = { path: ["tools", "web", "x_search", "apiKey"], message: @@ -12,6 +27,25 @@ const X_SEARCH_RULE: LegacyConfigRule = { }; export const LEGACY_CONFIG_MIGRATIONS_RUNTIME_PROVIDERS: LegacyConfigMigrationSpec[] = [ + defineLegacyConfigMigration({ + id: "plugins.allow->plugins.bundledDiscovery.compat", + describe: "Preserve bundled provider discovery for existing restrictive allowlists", + legacyRules: [BUNDLED_DISCOVERY_COMPAT_RULE], + apply: (raw, changes) => { + const plugins = isRecord(raw.plugins) ? raw.plugins : undefined; + if (!plugins || plugins.bundledDiscovery !== undefined) { + return; + } + const allow = plugins.allow; + if (!Array.isArray(allow) || allow.length === 0) { + return; + } + plugins.bundledDiscovery = "compat"; + changes.push( + 'Set plugins.bundledDiscovery="compat" to preserve legacy bundled provider discovery for this restrictive plugins.allow config.', + ); + }, + }), defineLegacyConfigMigration({ id: "tools.web.x_search.apiKey->plugins.entries.xai.config.webSearch.apiKey", describe: "Move legacy x_search auth into the xAI plugin webSearch config",