diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aefe4a87c8..0ed90d5b7c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Docs: https://docs.openclaw.ai - Release validation: install the cross-OS TypeScript harness through Windows-safe Node/npm shims so native Windows package checks reach the OpenClaw smoke suites instead of exiting before artifact capture. Thanks @vincentkoc. - Release validation: let Windows packaged-upgrade checks continue after the shipped 2026.5.2 updater hits its native-module swap cleanup fallback, verifying the fallback-installed candidate through package metadata and downstream smoke instead of crashing on the immediate update-status probe. Thanks @vincentkoc. - Doctor/plugins: skip channel-derived official plugin installs when another configured plugin is the effective owner for the same channel, so `doctor --repair` does not reinstall `feishu` while `openclaw-lark` handles `channels.feishu`. Fixes #76623. Thanks @fuyizheng3120. +- Agents/tools: use config-only runtime snapshots for plugin tool registration and live runtime config getters, avoiding expensive full secrets snapshot clones on the core-plugin-tools prep path. Fixes #76295. - Agents/bootstrap: keep pending `BOOTSTRAP.md` and bootstrap truncation notices in system-prompt Project Context instead of copying setup text or raw warning diagnostics into WebChat user/runtime context. Fixes #76946. - Channels/WhatsApp: allow `@whiskeysockets/libsignal-node` in `onlyBuiltDependencies` so pnpm v9+ `blockExoticSubdeps` no longer rejects the baileys git-tarball subdep and silences all inbound agent replies. Fixes #76539. Thanks @ottodeng and @vincentkoc. - Gateway/install: keep `.env`-managed values in the macOS LaunchAgent env file while still tracking `OPENCLAW_SERVICE_MANAGED_ENV_KEYS`, so regenerated services do not boot without managed auth/provider keys. Fixes #75374. diff --git a/src/agents/openclaw-plugin-tools.ts b/src/agents/openclaw-plugin-tools.ts index 4581d8a5beb..e9f555454c6 100644 --- a/src/agents/openclaw-plugin-tools.ts +++ b/src/agents/openclaw-plugin-tools.ts @@ -1,7 +1,10 @@ import { selectApplicableRuntimeConfig } from "../config/config.js"; +import { + getRuntimeConfigSnapshot, + getRuntimeConfigSourceSnapshot, +} from "../config/runtime-snapshot.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolvePluginTools } from "../plugins/tools.js"; -import { getActiveSecretsRuntimeSnapshot } from "../secrets/runtime.js"; import { normalizeDeliveryContext } from "../utils/delivery-context.js"; import { listProfilesForProvider } from "./auth-profiles.js"; import type { AuthProfileStore } from "./auth-profiles/types.js"; @@ -28,6 +31,27 @@ type ResolveOpenClawPluginToolsOptions = OpenClawPluginToolOptions & { authProfileStore?: AuthProfileStore; }; +function resolveApplicablePluginRuntimeConfig( + inputConfig?: OpenClawConfig, +): OpenClawConfig | undefined { + const runtimeConfig = getRuntimeConfigSnapshot() ?? undefined; + if (!runtimeConfig) { + return inputConfig; + } + if (!inputConfig || inputConfig === runtimeConfig) { + return runtimeConfig; + } + const runtimeSourceConfig = getRuntimeConfigSourceSnapshot() ?? undefined; + if (!runtimeSourceConfig) { + return inputConfig; + } + return selectApplicableRuntimeConfig({ + inputConfig, + runtimeConfig, + runtimeSourceConfig, + }); +} + export function resolveOpenClawPluginToolsForOptions(params: { options?: ResolveOpenClawPluginToolsOptions; resolvedConfig?: OpenClawConfig; @@ -45,12 +69,7 @@ export function resolveOpenClawPluginToolsForOptions(params: { }); const resolveCurrentRuntimeConfig = () => { - const currentRuntimeSnapshot = getActiveSecretsRuntimeSnapshot(); - return selectApplicableRuntimeConfig({ - inputConfig: params.resolvedConfig ?? params.options?.config, - runtimeConfig: currentRuntimeSnapshot?.config, - runtimeSourceConfig: currentRuntimeSnapshot?.sourceConfig, - }); + return resolveApplicablePluginRuntimeConfig(params.resolvedConfig ?? params.options?.config); }; const authProfileStore = params.options?.authProfileStore; const pluginTools = resolvePluginTools({ diff --git a/src/agents/openclaw-tools.browser-plugin.integration.test.ts b/src/agents/openclaw-tools.browser-plugin.integration.test.ts index 083e8c8cef9..2ab38080f25 100644 --- a/src/agents/openclaw-tools.browser-plugin.integration.test.ts +++ b/src/agents/openclaw-tools.browser-plugin.integration.test.ts @@ -1,5 +1,6 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; +import { resetConfigRuntimeState, setRuntimeConfigSnapshot } from "../config/config.js"; import { activateSecretsRuntimeSnapshot, clearSecretsRuntimeSnapshot } from "../secrets/runtime.js"; import { resolveOpenClawPluginToolsForOptions } from "./openclaw-plugin-tools.js"; @@ -15,6 +16,7 @@ describe("createOpenClawTools browser plugin integration", () => { afterEach(() => { hoisted.resolvePluginTools.mockReset(); clearSecretsRuntimeSnapshot(); + resetConfigRuntimeState(); }); it("keeps the browser tool returned by plugin resolution", () => { @@ -193,6 +195,48 @@ describe("createOpenClawTools browser plugin integration", () => { expect(capturedRuntimeConfig).toBe(resolvedRunConfig); }); + it("does not let a source-less pinned config snapshot override explicit plugin tool config", () => { + const pinnedRuntimeConfig = { + plugins: { + allow: ["old-plugin"], + }, + } as OpenClawConfig; + const explicitConfig = { + plugins: { + allow: ["browser"], + }, + tools: { + experimental: { + planTool: true, + }, + }, + } as OpenClawConfig; + let capturedRuntimeConfig: OpenClawConfig | undefined; + let getRuntimeConfig: (() => OpenClawConfig | undefined) | undefined; + hoisted.resolvePluginTools.mockImplementation((params: unknown) => { + const context = ( + params as { + context?: { + runtimeConfig?: OpenClawConfig; + getRuntimeConfig?: () => OpenClawConfig | undefined; + }; + } + ).context; + capturedRuntimeConfig = context?.runtimeConfig; + getRuntimeConfig = context?.getRuntimeConfig; + return []; + }); + setRuntimeConfigSnapshot(pinnedRuntimeConfig); + + resolveOpenClawPluginToolsForOptions({ + options: { config: explicitConfig }, + resolvedConfig: explicitConfig, + }); + + expect(capturedRuntimeConfig).toBe(explicitConfig); + expect(getRuntimeConfig?.()).toBe(explicitConfig); + }); + it("exposes a live runtime config getter to plugin tool factories", () => { const sourceConfig = { plugins: { @@ -218,23 +262,7 @@ describe("createOpenClawTools browser plugin integration", () => { ).context?.getRuntimeConfig; return []; }); - activateSecretsRuntimeSnapshot({ - sourceConfig, - config: firstRuntimeConfig, - authStores: [], - warnings: [], - webTools: { - search: { - providerSource: "none", - diagnostics: [], - }, - fetch: { - providerSource: "none", - diagnostics: [], - }, - diagnostics: [], - }, - }); + setRuntimeConfigSnapshot(firstRuntimeConfig, sourceConfig); resolveOpenClawPluginToolsForOptions({ options: { config: sourceConfig }, @@ -243,23 +271,7 @@ describe("createOpenClawTools browser plugin integration", () => { expect(getRuntimeConfig?.()).toStrictEqual(firstRuntimeConfig); - activateSecretsRuntimeSnapshot({ - sourceConfig, - config: nextRuntimeConfig, - authStores: [], - warnings: [], - webTools: { - search: { - providerSource: "none", - diagnostics: [], - }, - fetch: { - providerSource: "none", - diagnostics: [], - }, - diagnostics: [], - }, - }); + setRuntimeConfigSnapshot(nextRuntimeConfig, sourceConfig); expect(getRuntimeConfig?.()).toStrictEqual(nextRuntimeConfig); expect(getRuntimeConfig?.()?.plugins?.entries?.["memory-core"]?.enabled).toBe(false);