fix: guard memory doctor on active runtime

This commit is contained in:
Gustavo Madeira Santana
2026-04-20 21:14:54 -04:00
parent d493973eae
commit 1e4006b95e
2 changed files with 38 additions and 9 deletions

View File

@@ -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<CheckQmdBinaryAvailability>(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({

View File

@@ -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<RuntimeMemoryAuditContext | null> {
@@ -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):",