fix(memory-core): report active dreaming phases in status (#93113)

* fix(memory-core): report active dreaming phases in status

* fix(memory-core): repair active dreaming status phases

---------

Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
This commit is contained in:
xiayu
2026-06-23 07:16:14 +08:00
committed by GitHub
parent 2ce4a7483a
commit fc15c58715
2 changed files with 218 additions and 6 deletions

View File

@@ -6,6 +6,7 @@ import path from "node:path";
import type { MemoryEmbeddingProbeResult } from "openclaw/plugin-sdk/memory-core-host-engine-storage";
import {
resolveMemoryDreamingConfig,
resolveMemoryLightDreamingConfig,
resolveMemoryRemDreamingConfig,
} from "openclaw/plugin-sdk/memory-core-host-status";
import { buildAgentSessionKey } from "openclaw/plugin-sdk/routing";
@@ -223,12 +224,23 @@ async function createHistoricalRemHarnessWorkspace(params: {
function formatDreamingSummary(cfg: OpenClawConfig): string {
const pluginConfig = resolveMemoryPluginConfig(cfg);
const dreaming = resolveShortTermPromotionDreamingConfig({ pluginConfig, cfg });
if (!dreaming.enabled) {
return "off";
}
const timezone = dreaming.timezone ? ` (${dreaming.timezone})` : "";
return `${dreaming.cron}${timezone} · limit=${dreaming.limit} · minScore=${dreaming.minScore} · minRecallCount=${dreaming.minRecallCount} · minUniqueQueries=${dreaming.minUniqueQueries} · recencyHalfLifeDays=${dreaming.recencyHalfLifeDays} · maxAgeDays=${dreaming.maxAgeDays ?? "none"} · maxPromotedSnippetTokens=${dreaming.maxPromotedSnippetTokens}`;
const light = resolveMemoryLightDreamingConfig({ pluginConfig, cfg });
const deep = resolveShortTermPromotionDreamingConfig({ pluginConfig, cfg });
const rem = resolveMemoryRemDreamingConfig({ pluginConfig, cfg });
const timezone = deep.timezone ?? light.timezone ?? rem.timezone;
const formatCron = (cron: string) => (timezone ? `${cron} (${timezone})` : cron);
const lightSummary = light.enabled
? `light=${formatCron(light.cron)} · limit=${light.limit} · lookbackDays=${light.lookbackDays}`
: null;
const remSummary = rem.enabled
? `rem=${formatCron(rem.cron)} · limit=${rem.limit} · lookbackDays=${rem.lookbackDays} · minPatternStrength=${rem.minPatternStrength}`
: null;
const hasLighterPhase = light.enabled || rem.enabled;
const deepLabel = hasLighterPhase ? "deep=" : "";
const deepDetails = `${formatCron(deep.cron)} · limit=${deep.limit} · minScore=${deep.minScore} · minRecallCount=${deep.minRecallCount} · minUniqueQueries=${deep.minUniqueQueries} · recencyHalfLifeDays=${deep.recencyHalfLifeDays} · maxAgeDays=${deep.maxAgeDays ?? "none"} · maxPromotedSnippetTokens=${deep.maxPromotedSnippetTokens}`;
const deepSummary = deep.enabled ? `${deepLabel}${deepDetails}` : null;
const phases = [lightSummary, remSummary, deepSummary].filter(Boolean);
return phases.length > 0 ? phases.join(" · ") : "off";
}
function formatAuditCounts(audit: ShortTermAuditSummary): string {

View File

@@ -746,6 +746,206 @@ describe("memory cli", () => {
});
});
it("reports light-only dreaming as active during status", async () => {
getRuntimeConfig.mockReturnValue({
plugins: {
entries: {
"memory-core": {
config: {
dreaming: {
enabled: true,
frequency: "5 * * * *",
timezone: "UTC",
phases: {
light: {
enabled: true,
limit: 4,
lookbackDays: 2,
},
deep: {
enabled: false,
},
rem: {
enabled: false,
},
},
},
},
},
},
},
});
const close = vi.fn(async () => {});
mockManager({
probeVectorAvailability: vi.fn(async () => true),
status: () => makeMemoryStatus(),
close,
});
const log = spyRuntimeLogs(defaultRuntime);
await runMemoryCli(["status"]);
expectLogged(log, "Dreaming: light=5 * * * * (UTC) · limit=4 · lookbackDays=2");
expect(close).toHaveBeenCalled();
});
it("reports rem-only dreaming as active during status", async () => {
getRuntimeConfig.mockReturnValue({
plugins: {
entries: {
"memory-core": {
config: {
dreaming: {
enabled: true,
frequency: "0 6 * * 0",
timezone: "UTC",
phases: {
light: {
enabled: false,
},
deep: {
enabled: false,
},
rem: {
enabled: true,
limit: 3,
lookbackDays: 9,
minPatternStrength: 0.81,
},
},
},
},
},
},
},
});
const close = vi.fn(async () => {});
mockManager({
probeVectorAvailability: vi.fn(async () => true),
status: () => makeMemoryStatus(),
close,
});
const log = spyRuntimeLogs(defaultRuntime);
await runMemoryCli(["status"]);
expectLogged(
log,
"Dreaming: rem=0 6 * * 0 (UTC) · limit=3 · lookbackDays=9 · minPatternStrength=0.81",
);
expect(close).toHaveBeenCalled();
});
it("labels deep dreaming when multiple phases are active during status", async () => {
getRuntimeConfig.mockReturnValue({
plugins: {
entries: {
"memory-core": {
config: {
dreaming: {
enabled: true,
frequency: "15 2 * * *",
timezone: "UTC",
phases: {
light: {
enabled: true,
limit: 5,
lookbackDays: 1,
},
deep: {
enabled: true,
limit: 7,
minScore: 0.72,
minRecallCount: 4,
minUniqueQueries: 2,
recencyHalfLifeDays: 10,
maxAgeDays: 45,
maxPromotedSnippetTokens: 512,
},
rem: {
enabled: true,
limit: 2,
lookbackDays: 14,
minPatternStrength: 0.67,
},
},
},
},
},
},
},
});
const close = vi.fn(async () => {});
mockManager({
probeVectorAvailability: vi.fn(async () => true),
status: () => makeMemoryStatus(),
close,
});
const log = spyRuntimeLogs(defaultRuntime);
await runMemoryCli(["status"]);
expectLogged(log, "Dreaming: light=15 2 * * * (UTC) · limit=5 · lookbackDays=1");
expectLogged(log, "rem=15 2 * * * (UTC) · limit=2 · lookbackDays=14 · minPatternStrength=0.67");
expectLogged(log, "deep=15 2 * * * (UTC) · limit=7 · minScore=0.72");
expectLogged(log, "minRecallCount=4");
expectLogged(log, "maxPromotedSnippetTokens=512");
expect(close).toHaveBeenCalled();
});
it("preserves deep dreaming diagnostics during status", async () => {
getRuntimeConfig.mockReturnValue({
plugins: {
entries: {
"memory-core": {
config: {
dreaming: {
enabled: true,
frequency: "0 4 * * *",
timezone: "UTC",
phases: {
light: {
enabled: false,
},
deep: {
enabled: true,
limit: 6,
minScore: 0.88,
minRecallCount: 5,
minUniqueQueries: 3,
recencyHalfLifeDays: 12,
maxAgeDays: 30,
maxPromotedSnippetTokens: 640,
},
rem: {
enabled: false,
},
},
},
},
},
},
},
});
const close = vi.fn(async () => {});
mockManager({
probeVectorAvailability: vi.fn(async () => true),
status: () => makeMemoryStatus(),
close,
});
const log = spyRuntimeLogs(defaultRuntime);
await runMemoryCli(["status"]);
expectLogged(log, "Dreaming: 0 4 * * * (UTC) · limit=6 · minScore=0.88");
expectLogged(log, "minRecallCount=5");
expectLogged(log, "minUniqueQueries=3");
expectLogged(log, "recencyHalfLifeDays=12");
expectLogged(log, "maxAgeDays=30");
expectLogged(log, "maxPromotedSnippetTokens=640");
expect(close).toHaveBeenCalled();
});
it("repairs invalid recall metadata and stale locks with status --fix", async () => {
await withTempWorkspace(async (workspaceDir) => {
await shortTermTesting.writeRawRecallStore(workspaceDir, {