diff --git a/src/commands/doctor-memory-search.test.ts b/src/commands/doctor-memory-search.test.ts index 34f5d78f2db..672d52e2351 100644 --- a/src/commands/doctor-memory-search.test.ts +++ b/src/commands/doctor-memory-search.test.ts @@ -12,6 +12,7 @@ const resolveMemorySearchConfig = vi.hoisted(() => vi.fn()); const resolveApiKeyForProvider = vi.hoisted(() => vi.fn()); const hasAnyAuthProfileStoreSource = vi.hoisted(() => vi.fn(() => true)); const getActiveMemorySearchManager = vi.hoisted(() => vi.fn()); +const resolveActiveMemoryBackendConfig = vi.hoisted(() => vi.fn()); type CheckQmdBinaryAvailability = typeof checkQmdBinaryAvailabilityFn; const checkQmdBinaryAvailability = vi.hoisted(() => vi.fn(async () => ({ available: true })), @@ -47,6 +48,7 @@ vi.mock("../agents/auth-profiles.js", () => ({ vi.mock("../plugins/memory-runtime.js", () => ({ getActiveMemorySearchManager, + resolveActiveMemoryBackendConfig, })); vi.mock("../memory-host-sdk/engine-qmd.js", () => ({ @@ -153,6 +155,12 @@ describe("noteMemorySearchHealth", () => { hasAnyAuthProfileStoreSource.mockReset(); hasAnyAuthProfileStoreSource.mockReturnValue(true); getActiveMemorySearchManager.mockReset(); + resolveActiveMemoryBackendConfig.mockReset(); + resolveActiveMemoryBackendConfig.mockImplementation(({ cfg }: { cfg: OpenClawConfig }) => + cfg.memory?.backend === "qmd" + ? { backend: "qmd", qmd: cfg.memory.qmd ?? {} } + : { backend: "builtin" }, + ); getActiveMemorySearchManager.mockResolvedValue({ manager: { status: () => ({ workspaceDir: "/tmp/agent-default/workspace", backend: "builtin" }), @@ -219,6 +227,24 @@ describe("noteMemorySearchHealth", () => { expect(note).not.toHaveBeenCalled(); }); + it("does not emit provider guidance when no memory runtime is active", async () => { + resolveActiveMemoryBackendConfig.mockReturnValue(null); + resolveMemorySearchConfig.mockReturnValue({ + provider: "auto", + local: {}, + remote: {}, + }); + + await noteMemorySearchHealth(cfg, {}); + + expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(checkQmdBinaryAvailability).not.toHaveBeenCalled(); + expect(note).toHaveBeenCalledTimes(1); + expect(String(note.mock.calls[0]?.[0] ?? "")).toContain( + "No active memory plugin is registered", + ); + }); + it("does not warn when QMD backend is active", async () => { const qmdCfg = { memory: { backend: "qmd", qmd: { command: "qmd" } } } as OpenClawConfig; resolveMemorySearchConfig.mockReturnValue({ diff --git a/src/commands/doctor-memory-search.ts b/src/commands/doctor-memory-search.ts index 93a039757ee..6f903642a13 100644 --- a/src/commands/doctor-memory-search.ts +++ b/src/commands/doctor-memory-search.ts @@ -25,7 +25,10 @@ import { type DreamingArtifactsAuditSummary, type ShortTermAuditSummary, } from "../plugin-sdk/memory-core-engine-runtime.js"; -import { getActiveMemorySearchManager } from "../plugins/memory-runtime.js"; +import { + getActiveMemorySearchManager, + resolveActiveMemoryBackendConfig, +} from "../plugins/memory-runtime.js"; import { getProviderEnvVars } from "../secrets/provider-env-vars.js"; import { normalizeOptionalString } from "../shared/string-coerce.js"; import { note } from "../terminal/note.js"; @@ -124,10 +127,6 @@ function resolveSuggestedRemoteMemoryProvider(): string | undefined { )?.providerId; } -function resolveConfiguredQmdBackendConfig(cfg: OpenClawConfig): OpenClawConfig["memory"] | null { - return cfg.memory?.backend === "qmd" ? cfg.memory : null; -} - async function resolveRuntimeMemoryAuditContext( cfg: OpenClawConfig, ): Promise { @@ -325,17 +324,21 @@ export async function noteMemorySearchHealth( // QMD backend handles embeddings internally (e.g. embeddinggemma) — no // separate embedding provider is needed. Skip the provider check entirely. - const qmdBackendConfig = resolveConfiguredQmdBackendConfig(cfg); - if (qmdBackendConfig) { + const backendConfig = resolveActiveMemoryBackendConfig({ cfg, agentId }); + if (!backendConfig) { + note("No active memory plugin is registered for the current config.", "Memory search"); + return; + } + if (backendConfig.backend === "qmd") { const qmdCheck = await checkQmdBinaryAvailability({ - command: qmdBackendConfig.qmd?.command ?? "qmd", + command: backendConfig.qmd?.command ?? "qmd", env: process.env, cwd: resolveAgentWorkspaceDir(cfg, agentId), }); if (!qmdCheck.available) { note( [ - `QMD memory backend is configured, but the qmd binary could not be started (${qmdBackendConfig.qmd?.command ?? "qmd"}).`, + `QMD memory backend is configured, but the qmd binary could not be started (${backendConfig.qmd?.command ?? "qmd"}).`, qmdCheck.error ? `Probe error: ${qmdCheck.error}` : null, "", "Fix (pick one):",