fix(memory): refresh tool config at execution

This commit is contained in:
Peter Steinberger
2026-04-27 11:35:51 +01:00
parent fa468d0c2d
commit 8a8cc8dc9f
6 changed files with 74 additions and 13 deletions

View File

@@ -20,6 +20,7 @@ Docs: https://docs.openclaw.ai
### Fixes ### Fixes
- Memory-core: re-resolve the active runtime config whenever `memory_search` or `memory_get` executes, so provider changes made by `config.patch` stop leaving stale embedding backends behind in existing tool instances. Fixes #61098. Thanks @BradGroux and @Linux2010.
- Channels/setup: treat bundled channel plugins as already bundled during `channels add` and onboarding, enabling them without writing redundant `plugins.load.paths` entries or path install records. Fixes #72740. Thanks @iCodePoet. - Channels/setup: treat bundled channel plugins as already bundled during `channels add` and onboarding, enabling them without writing redundant `plugins.load.paths` entries or path install records. Fixes #72740. Thanks @iCodePoet.
- WhatsApp: honor gateway `HTTPS_PROXY` / `HTTP_PROXY` env vars for QR-login WebSocket connections, while respecting `NO_PROXY`, so proxied networks no longer fall back to direct `mmg.whatsapp.net` connections that time out with 408. Fixes #72547; supersedes #72692. Thanks @mebusw and @SymbolStar. - WhatsApp: honor gateway `HTTPS_PROXY` / `HTTP_PROXY` env vars for QR-login WebSocket connections, while respecting `NO_PROXY`, so proxied networks no longer fall back to direct `mmg.whatsapp.net` connections that time out with 408. Fixes #72547; supersedes #72692. Thanks @mebusw and @SymbolStar.
- Bonjour: default mDNS advertisements to the system hostname when it is DNS-safe, avoiding `openclaw.local` probing conflicts and Gateway restart loops on hosts such as `Lobster` or `ubuntu`. Fixes #72355 and #72689; supersedes #72694. Thanks @mscheuerlein-bot, @gcusms, @moyuwuhen601, @pavan987, @zml-0912, @hhq365, and @SymbolStar. - Bonjour: default mDNS advertisements to the system hostname when it is DNS-safe, avoiding `openclaw.local` probing conflicts and Gateway restart loops on hosts such as `Lobster` or `ubuntu`. Fixes #72355 and #72689; supersedes #72694. Thanks @mscheuerlein-bot, @gcusms, @moyuwuhen601, @pavan987, @zml-0912, @hhq365, and @SymbolStar.

View File

@@ -1,3 +1,4 @@
import { getRuntimeConfigSnapshot } from "openclaw/plugin-sdk/config-runtime";
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { registerMemoryCli } from "./src/cli.js"; import { registerMemoryCli } from "./src/cli.js";
import { registerDreamingCommand } from "./src/dreaming-command.js"; import { registerDreamingCommand } from "./src/dreaming-command.js";
@@ -42,7 +43,8 @@ export default definePluginEntry({
api.registerTool( api.registerTool(
(ctx) => (ctx) =>
createMemorySearchTool({ createMemorySearchTool({
config: ctx.config, config: ctx.runtimeConfig ?? ctx.config,
getConfig: () => getRuntimeConfigSnapshot() ?? ctx.runtimeConfig ?? ctx.config,
agentSessionKey: ctx.sessionKey, agentSessionKey: ctx.sessionKey,
sandboxed: ctx.sandboxed, sandboxed: ctx.sandboxed,
}), }),
@@ -52,7 +54,8 @@ export default definePluginEntry({
api.registerTool( api.registerTool(
(ctx) => (ctx) =>
createMemoryGetTool({ createMemoryGetTool({
config: ctx.config, config: ctx.runtimeConfig ?? ctx.config,
getConfig: () => getRuntimeConfigSnapshot() ?? ctx.runtimeConfig ?? ctx.config,
agentSessionKey: ctx.sessionKey, agentSessionKey: ctx.sessionKey,
}), }),
{ names: ["memory_get"] }, { names: ["memory_get"] },

View File

@@ -52,7 +52,9 @@ const stubManager = {
close: vi.fn(), close: vi.fn(),
}; };
const getMemorySearchManagerMock = vi.fn(async () => ({ manager: stubManager })); const getMemorySearchManagerMock = vi.fn(async (_params: { cfg?: unknown }) => ({
manager: stubManager,
}));
const readAgentMemoryFileMock = vi.fn( const readAgentMemoryFileMock = vi.fn(
async (params: MemoryReadParams) => await readFileImpl(params), async (params: MemoryReadParams) => await readFileImpl(params),
); );
@@ -116,6 +118,10 @@ export function getMemorySearchManagerMockCalls(): number {
return getMemorySearchManagerMock.mock.calls.length; return getMemorySearchManagerMock.mock.calls.length;
} }
export function getMemorySearchManagerMockConfigs(): unknown[] {
return getMemorySearchManagerMock.mock.calls.map(([params]) => params.cfg);
}
export function getReadAgentMemoryFileMockCalls(): number { export function getReadAgentMemoryFileMockCalls(): number {
return readAgentMemoryFileMock.mock.calls.length; return readAgentMemoryFileMock.mock.calls.length;
} }

View File

@@ -13,6 +13,11 @@ type MemoryToolRuntime = typeof import("./tools.runtime.js");
type MemorySearchManagerResult = Awaited< type MemorySearchManagerResult = Awaited<
ReturnType<(typeof import("./memory/index.js"))["getMemorySearchManager"]> ReturnType<(typeof import("./memory/index.js"))["getMemorySearchManager"]>
>; >;
type MemoryToolOptions = {
config?: OpenClawConfig;
getConfig?: () => OpenClawConfig | undefined;
agentSessionKey?: string;
};
let memoryToolRuntimePromise: Promise<MemoryToolRuntime> | null = null; let memoryToolRuntimePromise: Promise<MemoryToolRuntime> | null = null;
@@ -44,11 +49,8 @@ export const MemoryGetSchema = Type.Object({
), ),
}); });
export function resolveMemoryToolContext(options: { export function resolveMemoryToolContext(options: MemoryToolOptions) {
config?: OpenClawConfig; const cfg = options.getConfig?.() ?? options.config;
agentSessionKey?: string;
}) {
const cfg = options.config;
if (!cfg) { if (!cfg) {
return null; return null;
} }
@@ -98,10 +100,7 @@ export async function getMemoryManagerContextWithPurpose(params: {
} }
export function createMemoryTool(params: { export function createMemoryTool(params: {
options: { options: MemoryToolOptions;
config?: OpenClawConfig;
agentSessionKey?: string;
};
label: string; label: string;
name: string; name: string;
description: string; description: string;
@@ -117,7 +116,10 @@ export function createMemoryTool(params: {
name: params.name, name: params.name,
description: params.description, description: params.description,
parameters: params.parameters, parameters: params.parameters,
execute: params.execute(ctx), execute: async (toolCallId, toolParams) => {
const latestCtx = resolveMemoryToolContext(params.options) ?? ctx;
return await params.execute(latestCtx)(toolCallId, toolParams);
},
}; };
} }

View File

@@ -1,10 +1,13 @@
import { beforeEach, describe, expect, it } from "vitest"; import { beforeEach, describe, expect, it } from "vitest";
import { import {
getMemorySearchManagerMockConfigs,
resetMemoryToolMockState, resetMemoryToolMockState,
setMemoryBackend, setMemoryBackend,
setMemorySearchImpl, setMemorySearchImpl,
} from "./memory-tool-manager-mock.js"; } from "./memory-tool-manager-mock.js";
import { createMemorySearchTool } from "./tools.js";
import { import {
asOpenClawConfig,
createMemorySearchToolOrThrow, createMemorySearchToolOrThrow,
expectUnavailableMemorySearchDetails, expectUnavailableMemorySearchDetails,
} from "./tools.test-helpers.js"; } from "./tools.test-helpers.js";
@@ -103,4 +106,48 @@ describe("memory_search unavailable payloads", () => {
expect.any(Number), expect.any(Number),
); );
}); });
it("re-resolves config when executing a previously created tool", async () => {
const startupConfig = asOpenClawConfig({
agents: {
defaults: {
memorySearch: {
provider: "ollama",
model: "nomic-embed-text",
},
},
list: [{ id: "main", default: true }],
},
memory: {
backend: "builtin",
},
});
const patchedConfig = asOpenClawConfig({
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
},
},
list: [{ id: "main", default: true }],
},
memory: {
backend: "builtin",
},
});
let liveConfig = startupConfig;
const tool = createMemorySearchTool({
config: startupConfig,
getConfig: () => liveConfig,
});
if (!tool) {
throw new Error("tool missing");
}
liveConfig = patchedConfig;
await tool.execute("patched-config", { query: "provider switch" });
expect(getMemorySearchManagerMockConfigs()).toEqual([patchedConfig]);
});
}); });

View File

@@ -182,6 +182,7 @@ async function executeMemoryReadResult<T>(params: {
export function createMemorySearchTool(options: { export function createMemorySearchTool(options: {
config?: OpenClawConfig; config?: OpenClawConfig;
getConfig?: () => OpenClawConfig | undefined;
agentSessionKey?: string; agentSessionKey?: string;
sandboxed?: boolean; sandboxed?: boolean;
}) { }) {
@@ -344,6 +345,7 @@ export function createMemorySearchTool(options: {
export function createMemoryGetTool(options: { export function createMemoryGetTool(options: {
config?: OpenClawConfig; config?: OpenClawConfig;
getConfig?: () => OpenClawConfig | undefined;
agentSessionKey?: string; agentSessionKey?: string;
}) { }) {
return createMemoryTool({ return createMemoryTool({