diff --git a/CHANGELOG.md b/CHANGELOG.md index cfd555ee12f..bdbbe2a2311 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,7 @@ Docs: https://docs.openclaw.ai - TUI/activation: validate `/activation` arguments in the TUI and reject invalid values instead of silently coercing them to `mention`. (#55733) Thanks @shakkernerd. - Agents/model switching: apply `/model` changes to active embedded runs at the next safe retry boundary, so overloaded or retrying turns switch to the newly selected model instead of staying pinned to the old provider. - Agents/Codex fallback: classify Codex `server_error` payloads as failoverable, sanitize `Codex error:` payloads before they reach chat, preserve context-overflow guidance for prefixed `invalid_request_error` payloads, and omit provider `request_id` values from user-facing UI copy. (#42892) Thanks @xaeon2026. +- Memory/search: share memory embedding provider registrations across split plugin runtimes so memory search no longer fails with unknown provider errors after memory-core registers built-in adapters. (#55945) Thanks @glitch418x. ## 2026.3.24 diff --git a/src/plugins/memory-embedding-providers.test.ts b/src/plugins/memory-embedding-providers.test.ts index 6485cc16798..e64b975f8cd 100644 --- a/src/plugins/memory-embedding-providers.test.ts +++ b/src/plugins/memory-embedding-providers.test.ts @@ -11,6 +11,8 @@ import { type MemoryEmbeddingProviderAdapter, } from "./memory-embedding-providers.js"; +const MEMORY_EMBEDDING_PROVIDERS_KEY = Symbol.for("openclaw.memoryEmbeddingProviders"); + function createAdapter(id: string): MemoryEmbeddingProviderAdapter { return { id, @@ -81,4 +83,18 @@ describe("memory embedding provider registry", () => { expect(listMemoryEmbeddingProviders()).toEqual([]); }); + + it("stores adapters in a process-global singleton map", () => { + const alpha = createAdapter("alpha"); + registerMemoryEmbeddingProvider(alpha, { ownerPluginId: "memory-core" }); + + const globalRegistry = (globalThis as Record)[ + MEMORY_EMBEDDING_PROVIDERS_KEY + ] as Map; + + expect(globalRegistry.get("alpha")).toEqual({ + adapter: alpha, + ownerPluginId: "memory-core", + }); + }); }); diff --git a/src/plugins/memory-embedding-providers.ts b/src/plugins/memory-embedding-providers.ts index fd1edd09b32..167773ae0df 100644 --- a/src/plugins/memory-embedding-providers.ts +++ b/src/plugins/memory-embedding-providers.ts @@ -72,13 +72,24 @@ export type RegisteredMemoryEmbeddingProvider = { ownerPluginId?: string; }; -const memoryEmbeddingProviders = new Map(); +const MEMORY_EMBEDDING_PROVIDERS_KEY = Symbol.for("openclaw.memoryEmbeddingProviders"); + +function getMemoryEmbeddingProviders(): Map { + const globalStore = globalThis as Record; + const existing = globalStore[MEMORY_EMBEDDING_PROVIDERS_KEY]; + if (existing instanceof Map) { + return existing as Map; + } + const created = new Map(); + globalStore[MEMORY_EMBEDDING_PROVIDERS_KEY] = created; + return created; +} export function registerMemoryEmbeddingProvider( adapter: MemoryEmbeddingProviderAdapter, options?: { ownerPluginId?: string }, ): void { - memoryEmbeddingProviders.set(adapter.id, { + getMemoryEmbeddingProviders().set(adapter.id, { adapter, ownerPluginId: options?.ownerPluginId, }); @@ -87,15 +98,15 @@ export function registerMemoryEmbeddingProvider( export function getRegisteredMemoryEmbeddingProvider( id: string, ): RegisteredMemoryEmbeddingProvider | undefined { - return memoryEmbeddingProviders.get(id); + return getMemoryEmbeddingProviders().get(id); } export function getMemoryEmbeddingProvider(id: string): MemoryEmbeddingProviderAdapter | undefined { - return memoryEmbeddingProviders.get(id)?.adapter; + return getMemoryEmbeddingProviders().get(id)?.adapter; } export function listRegisteredMemoryEmbeddingProviders(): RegisteredMemoryEmbeddingProvider[] { - return Array.from(memoryEmbeddingProviders.values()); + return Array.from(getMemoryEmbeddingProviders().values()); } export function listMemoryEmbeddingProviders(): MemoryEmbeddingProviderAdapter[] { @@ -103,7 +114,7 @@ export function listMemoryEmbeddingProviders(): MemoryEmbeddingProviderAdapter[] } export function restoreMemoryEmbeddingProviders(adapters: MemoryEmbeddingProviderAdapter[]): void { - memoryEmbeddingProviders.clear(); + getMemoryEmbeddingProviders().clear(); for (const adapter of adapters) { registerMemoryEmbeddingProvider(adapter); } @@ -112,7 +123,7 @@ export function restoreMemoryEmbeddingProviders(adapters: MemoryEmbeddingProvide export function restoreRegisteredMemoryEmbeddingProviders( entries: RegisteredMemoryEmbeddingProvider[], ): void { - memoryEmbeddingProviders.clear(); + getMemoryEmbeddingProviders().clear(); for (const entry of entries) { registerMemoryEmbeddingProvider(entry.adapter, { ownerPluginId: entry.ownerPluginId, @@ -121,7 +132,7 @@ export function restoreRegisteredMemoryEmbeddingProviders( } export function clearMemoryEmbeddingProviders(): void { - memoryEmbeddingProviders.clear(); + getMemoryEmbeddingProviders().clear(); } export const _resetMemoryEmbeddingProviders = clearMemoryEmbeddingProviders;