From d686e6f876b45e2dc14d9126c71c09ca0d3e5a6f Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 22 Apr 2026 16:09:07 -0700 Subject: [PATCH] fix(hooks): avoid stale active-memory startup fallback --- CHANGELOG.md | 1 + extensions/active-memory/index.test.ts | 21 +++++++++++++++++++ extensions/active-memory/index.ts | 15 ++++++++----- .../contracts/boundary-invariants.test.ts | 5 +---- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fda5bfa3cc9..a4213266595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ Docs: https://docs.openclaw.ai - ACP/sessions_spawn: honor explicit `model` overrides for ACP child sessions instead of silently falling back to the target agent default model. (#70210) Thanks @felix-miao. - Diffs/viewer: re-read remote viewer access policy from live runtime config on each request, so toggling `plugins.entries.diffs.config.security.allowRemoteViewer` closes proxied viewer access immediately instead of waiting for a restart. Thanks @vincentkoc. - Memory/LanceDB: stop resurrecting removed live `memory-lancedb` hook config from startup snapshots, so deleting or disabling the plugin entry shuts off auto-recall and auto-capture without a restart. Thanks @vincentkoc. +- Active Memory: stop reviving removed live `active-memory` config from startup snapshots, so removing the plugin entry turns the hook off immediately instead of waiting for a restart. Thanks @vincentkoc. - Agents/subagents: drop bare `NO_REPLY` from the parent turn when the session still has pending spawned children, so direct-conversation surfaces such as Telegram DMs no longer rewrite the sentinel into visible fallback chatter while waiting for the child completion event. (#69942) Thanks @neeravmakwana. - Plugins/install: keep bundled plugin dependencies off npm install while repairing them when plugins activate from a packaged install, including Feishu/Lark, Browser, and direct bundled channel setup-entry loads. - CLI/channels: skip and cache bundled channel plugin, setup, and secrets load failures during read-only discovery, so one broken unused bundled channel cannot crash `openclaw status` or bootstrap secret scans. diff --git a/extensions/active-memory/index.test.ts b/extensions/active-memory/index.test.ts index ae3f006afcf..072b2b22b1c 100644 --- a/extensions/active-memory/index.test.ts +++ b/extensions/active-memory/index.test.ts @@ -365,6 +365,27 @@ describe("active-memory plugin", () => { expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); }); + it("fails closed when the live active-memory plugin entry is removed", async () => { + configFile = { + plugins: { + entries: {}, + }, + }; + + const result = await hooks.before_prompt_build( + { prompt: "what wings should i order after active memory is removed?", messages: [] }, + { + agentId: "main", + trigger: "user", + sessionKey: "agent:main:live-config-removed", + messageProvider: "webchat", + }, + ); + + expect(result).toBeUndefined(); + expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); + }); + it("does not run for agents that are not explicitly targeted", async () => { const result = await hooks.before_prompt_build( { prompt: "what wings should i order?", messages: [] }, diff --git a/extensions/active-memory/index.ts b/extensions/active-memory/index.ts index e83e100f6c9..a7afcf84c29 100644 --- a/extensions/active-memory/index.ts +++ b/extensions/active-memory/index.ts @@ -9,6 +9,7 @@ import { resolveAgentWorkspaceDir, } from "openclaw/plugin-sdk/agent-runtime"; import { + resolveLivePluginConfigObject, resolvePluginConfigObject, resolveSessionStoreEntry, updateSessionStore, @@ -1883,11 +1884,15 @@ export default definePluginEntry({ }; warnDeprecatedModelFallbackPolicy(api.pluginConfig); const refreshLiveConfigFromRuntime = () => { - const livePluginConfig = - resolvePluginConfigObject(api.runtime.config.loadConfig(), "active-memory") ?? - api.pluginConfig; - config = normalizePluginConfig(livePluginConfig); - warnDeprecatedModelFallbackPolicy(livePluginConfig); + const livePluginConfig = resolveLivePluginConfigObject( + api.runtime.config?.loadConfig, + "active-memory", + api.pluginConfig as Record, + ); + config = normalizePluginConfig(livePluginConfig ?? { enabled: false }); + if (livePluginConfig) { + warnDeprecatedModelFallbackPolicy(livePluginConfig); + } }; api.registerCommand({ name: "active-memory", diff --git a/src/plugins/contracts/boundary-invariants.test.ts b/src/plugins/contracts/boundary-invariants.test.ts index f92d92fb0ee..3be4f07b5a5 100644 --- a/src/plugins/contracts/boundary-invariants.test.ts +++ b/src/plugins/contracts/boundary-invariants.test.ts @@ -47,10 +47,7 @@ const BUNDLED_TYPED_HOOK_REGISTRATION_GUARDS = { readonly string[] >; const BUNDLED_LIVE_CONFIG_HOOK_GUARDS = { - "extensions/active-memory/index.ts": [ - 'resolvePluginConfigObject(api.runtime.config.loadConfig(), "active-memory")', - "api.runtime.config.loadConfig()", - ], + "extensions/active-memory/index.ts": ["resolveLivePluginConfigObject(", '"active-memory"'], "extensions/diffs/src/plugin.ts": [ 'resolvePluginConfigObject(currentConfig, "diffs")', "api.runtime.config?.loadConfig?.() ?? api.config",