diff --git a/CHANGELOG.md b/CHANGELOG.md index 168d8ea852f..1568496abf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ Docs: https://docs.openclaw.ai - Feishu/Plugins: restore bundled Feishu SDK availability for global installs and strip `openclaw: workspace:*` from plugin `devDependencies` during plugin-version sync so npm-installed Feishu plugins do not fail dependency install. (#23611, #23645, #23603) - Plugins/Install: strip `workspace:*` devDependency entries from copied plugin manifests before `npm install --omit=dev`, preventing `EUNSUPPORTEDPROTOCOL` install failures for npm-published channel plugins (including Feishu and MS Teams). - Config/Channels: auto-enable built-in channels by writing `channels..enabled=true` (not `plugins.entries.`), and stop adding built-ins to `plugins.allow`, preventing `plugins.entries.telegram: plugin not found` validation failures. +- Config/Channels: when `plugins.allow` is active, auto-enable/enable flows now also allowlist configured built-in channels so `channels..enabled=true` cannot remain blocked by restrictive plugin allowlists. - Plugins/Discovery: ignore scanned extension backup/disabled directory patterns (for example `.backup-*`, `.bak`, `.disabled*`) and move updater backup directories under `.openclaw-install-backups`, preventing duplicate plugin-id collisions from archived copies. - Dev tooling: prevent `CLAUDE.md` symlink target regressions by excluding CLAUDE symlink sentinels from `oxfmt` and marking them `-text` in `.gitattributes`, so formatter/EOL normalization cannot reintroduce trailing-newline targets. Thanks @vincentkoc. - Cron: honor `cron.maxConcurrentRuns` in the timer loop so due jobs can execute up to the configured parallelism instead of always running serially. (#11595) Thanks @Takhoffman. diff --git a/src/config/plugin-auto-enable.test.ts b/src/config/plugin-auto-enable.test.ts index 284aea923dd..a0979b537d0 100644 --- a/src/config/plugin-auto-enable.test.ts +++ b/src/config/plugin-auto-enable.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import { applyPluginAutoEnable } from "./plugin-auto-enable.js"; describe("applyPluginAutoEnable", () => { - it("auto-enables built-in channels without touching plugins allowlist", () => { + it("auto-enables built-in channels and appends to existing allowlist", () => { const result = applyPluginAutoEnable({ config: { channels: { slack: { botToken: "x" } }, @@ -13,10 +13,22 @@ describe("applyPluginAutoEnable", () => { expect(result.config.channels?.slack?.enabled).toBe(true); expect(result.config.plugins?.entries?.slack).toBeUndefined(); - expect(result.config.plugins?.allow).toEqual(["telegram"]); + expect(result.config.plugins?.allow).toEqual(["telegram", "slack"]); expect(result.changes.join("\n")).toContain("Slack configured, enabled automatically."); }); + it("does not create plugins.allow when allowlist is unset", () => { + const result = applyPluginAutoEnable({ + config: { + channels: { slack: { botToken: "x" } }, + }, + env: {}, + }); + + expect(result.config.channels?.slack?.enabled).toBe(true); + expect(result.config.plugins?.allow).toBeUndefined(); + }); + it("ignores channels.modelByChannel for plugin auto-enable", () => { const result = applyPluginAutoEnable({ config: { diff --git a/src/config/plugin-auto-enable.ts b/src/config/plugin-auto-enable.ts index 46365fc7853..50fb9dac90a 100644 --- a/src/config/plugin-auto-enable.ts +++ b/src/config/plugin-auto-enable.ts @@ -477,8 +477,7 @@ export function applyPluginAutoEnable(params: { continue; } const allow = next.plugins?.allow; - const allowMissing = - !builtInChannelId && Array.isArray(allow) && !allow.includes(entry.pluginId); + const allowMissing = Array.isArray(allow) && !allow.includes(entry.pluginId); const alreadyEnabled = builtInChannelId != null ? (() => { @@ -498,7 +497,7 @@ export function applyPluginAutoEnable(params: { continue; } next = registerPluginEntry(next, entry.pluginId); - if (!builtInChannelId) { + if (allowMissing || !builtInChannelId) { next = ensurePluginAllowlisted(next, entry.pluginId); } changes.push(formatAutoEnableChange(entry)); diff --git a/src/plugins/enable.test.ts b/src/plugins/enable.test.ts index 73dbfce8462..5bdac4d1851 100644 --- a/src/plugins/enable.test.ts +++ b/src/plugins/enable.test.ts @@ -39,4 +39,16 @@ describe("enablePluginInConfig", () => { expect(result.config.channels?.telegram?.enabled).toBe(true); expect(result.config.plugins?.entries?.telegram).toBeUndefined(); }); + + it("adds built-in channel id to allowlist when allowlist is configured", () => { + const cfg: OpenClawConfig = { + plugins: { + allow: ["memory-core"], + }, + }; + const result = enablePluginInConfig(cfg, "telegram"); + expect(result.enabled).toBe(true); + expect(result.config.channels?.telegram?.enabled).toBe(true); + expect(result.config.plugins?.allow).toEqual(["memory-core", "telegram"]); + }); }); diff --git a/src/plugins/enable.ts b/src/plugins/enable.ts index 6df4a6cbe01..55bd8927976 100644 --- a/src/plugins/enable.ts +++ b/src/plugins/enable.ts @@ -24,19 +24,18 @@ export function enablePluginInConfig(cfg: OpenClawConfig, pluginId: string): Plu existing && typeof existing === "object" && !Array.isArray(existing) ? (existing as Record) : {}; - return { - config: { - ...cfg, - channels: { - ...cfg.channels, - [builtInChannelId]: { - ...existingRecord, - enabled: true, - }, + let next: OpenClawConfig = { + ...cfg, + channels: { + ...cfg.channels, + [builtInChannelId]: { + ...existingRecord, + enabled: true, }, }, - enabled: true, }; + next = ensurePluginAllowlisted(next, resolvedId); + return { config: next, enabled: true }; } const entries = {