mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 16:50:23 +00:00
Dreaming: simplify sweep flow and add diary surface
This commit is contained in:
@@ -34,6 +34,7 @@ describe("memory dreaming host helpers", () => {
|
||||
pluginConfig: {
|
||||
dreaming: {
|
||||
enabled: true,
|
||||
frequency: "0 */4 * * *",
|
||||
timezone: "Europe/London",
|
||||
storage: {
|
||||
mode: "both",
|
||||
@@ -41,7 +42,6 @@ describe("memory dreaming host helpers", () => {
|
||||
},
|
||||
phases: {
|
||||
deep: {
|
||||
cron: "0 */4 * * *",
|
||||
limit: "5",
|
||||
minScore: "0.9",
|
||||
minRecallCount: "4",
|
||||
@@ -54,8 +54,8 @@ describe("memory dreaming host helpers", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(resolved.mode).toBe("core");
|
||||
expect(resolved.enabled).toBe(true);
|
||||
expect(resolved.frequency).toBe("0 3 * * *");
|
||||
expect(resolved.timezone).toBe("Europe/London");
|
||||
expect(resolved.storage).toEqual({
|
||||
mode: "both",
|
||||
@@ -86,18 +86,34 @@ describe("memory dreaming host helpers", () => {
|
||||
cfg,
|
||||
});
|
||||
|
||||
expect(resolved.mode).toBe("off");
|
||||
expect(resolved.enabled).toBe(false);
|
||||
expect(resolved.frequency).toBe("0 3 * * *");
|
||||
expect(resolved.timezone).toBe("America/Los_Angeles");
|
||||
expect(resolved.phases.deep).toMatchObject({
|
||||
cron: "0 3 * * *",
|
||||
limit: 10,
|
||||
minScore: 0.75,
|
||||
minScore: 0.8,
|
||||
recencyHalfLifeDays: 14,
|
||||
maxAgeDays: 30,
|
||||
});
|
||||
});
|
||||
|
||||
it("applies top-level dreaming frequency across all phases", () => {
|
||||
const resolved = resolveMemoryDreamingConfig({
|
||||
pluginConfig: {
|
||||
dreaming: {
|
||||
enabled: true,
|
||||
frequency: "15 */8 * * *",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(resolved.frequency).toBe("15 */8 * * *");
|
||||
expect(resolved.phases.light.cron).toBe("15 */8 * * *");
|
||||
expect(resolved.phases.deep.cron).toBe("15 */8 * * *");
|
||||
expect(resolved.phases.rem.cron).toBe("15 */8 * * *");
|
||||
});
|
||||
|
||||
it("dedupes shared workspaces and skips agents without memory search", () => {
|
||||
resolveMemorySearchConfig.mockImplementation((_cfg: OpenClawConfig, agentId: string) =>
|
||||
agentId === "beta" ? null : { enabled: true },
|
||||
|
||||
@@ -8,8 +8,7 @@ export const DEFAULT_MEMORY_DREAMING_TIMEZONE = undefined;
|
||||
export const DEFAULT_MEMORY_DREAMING_VERBOSE_LOGGING = false;
|
||||
export const DEFAULT_MEMORY_DREAMING_STORAGE_MODE = "inline";
|
||||
export const DEFAULT_MEMORY_DREAMING_SEPARATE_REPORTS = false;
|
||||
export const DEFAULT_MEMORY_DREAMING_MODE = "off";
|
||||
export const DEFAULT_MEMORY_DREAMING_PRESET = "core";
|
||||
export const DEFAULT_MEMORY_DREAMING_FREQUENCY = "0 3 * * *";
|
||||
|
||||
export const DEFAULT_MEMORY_LIGHT_DREAMING_CRON_EXPR = "0 */6 * * *";
|
||||
export const DEFAULT_MEMORY_LIGHT_DREAMING_LOOKBACK_DAYS = 2;
|
||||
@@ -18,9 +17,9 @@ export const DEFAULT_MEMORY_LIGHT_DREAMING_DEDUPE_SIMILARITY = 0.9;
|
||||
|
||||
export const DEFAULT_MEMORY_DEEP_DREAMING_CRON_EXPR = "0 3 * * *";
|
||||
export const DEFAULT_MEMORY_DEEP_DREAMING_LIMIT = 10;
|
||||
export const DEFAULT_MEMORY_DEEP_DREAMING_MIN_SCORE = 0.75;
|
||||
export const DEFAULT_MEMORY_DEEP_DREAMING_MIN_SCORE = 0.8;
|
||||
export const DEFAULT_MEMORY_DEEP_DREAMING_MIN_RECALL_COUNT = 3;
|
||||
export const DEFAULT_MEMORY_DEEP_DREAMING_MIN_UNIQUE_QUERIES = 2;
|
||||
export const DEFAULT_MEMORY_DEEP_DREAMING_MIN_UNIQUE_QUERIES = 3;
|
||||
export const DEFAULT_MEMORY_DEEP_DREAMING_RECENCY_HALF_LIFE_DAYS = 14;
|
||||
export const DEFAULT_MEMORY_DEEP_DREAMING_MAX_AGE_DAYS = 30;
|
||||
|
||||
@@ -44,8 +43,6 @@ export type MemoryDreamingSpeed = "fast" | "balanced" | "slow";
|
||||
export type MemoryDreamingThinking = "low" | "medium" | "high";
|
||||
export type MemoryDreamingBudget = "cheap" | "medium" | "expensive";
|
||||
export type MemoryDreamingStorageMode = "inline" | "separate" | "both";
|
||||
export type MemoryDreamingPreset = "core" | "deep" | "rem";
|
||||
export type MemoryDreamingMode = MemoryDreamingPreset | "off";
|
||||
|
||||
export type MemoryLightDreamingSource = "daily" | "sessions" | "recall";
|
||||
export type MemoryDeepDreamingSource = "daily" | "memory" | "sessions" | "logs" | "recall";
|
||||
@@ -112,8 +109,8 @@ export type MemoryRemDreamingConfig = {
|
||||
export type MemoryDreamingPhaseName = "light" | "deep" | "rem";
|
||||
|
||||
export type MemoryDreamingConfig = {
|
||||
mode: MemoryDreamingMode;
|
||||
enabled: boolean;
|
||||
frequency: string;
|
||||
timezone?: string;
|
||||
verboseLogging: boolean;
|
||||
storage: MemoryDreamingStorageConfig;
|
||||
@@ -146,47 +143,6 @@ const DEFAULT_MEMORY_DEEP_DREAMING_SOURCES: MemoryDeepDreamingSource[] = [
|
||||
];
|
||||
const DEFAULT_MEMORY_REM_DREAMING_SOURCES: MemoryRemDreamingSource[] = ["memory", "daily", "deep"];
|
||||
|
||||
const MEMORY_DREAMING_PRESET_DEFAULTS: Record<
|
||||
MemoryDreamingPreset,
|
||||
{
|
||||
cron: string;
|
||||
limit: number;
|
||||
minScore: number;
|
||||
minRecallCount: number;
|
||||
minUniqueQueries: number;
|
||||
recencyHalfLifeDays: number;
|
||||
maxAgeDays: number;
|
||||
}
|
||||
> = {
|
||||
core: {
|
||||
cron: DEFAULT_MEMORY_DEEP_DREAMING_CRON_EXPR,
|
||||
limit: DEFAULT_MEMORY_DEEP_DREAMING_LIMIT,
|
||||
minScore: DEFAULT_MEMORY_DEEP_DREAMING_MIN_SCORE,
|
||||
minRecallCount: DEFAULT_MEMORY_DEEP_DREAMING_MIN_RECALL_COUNT,
|
||||
minUniqueQueries: DEFAULT_MEMORY_DEEP_DREAMING_MIN_UNIQUE_QUERIES,
|
||||
recencyHalfLifeDays: DEFAULT_MEMORY_DEEP_DREAMING_RECENCY_HALF_LIFE_DAYS,
|
||||
maxAgeDays: DEFAULT_MEMORY_DEEP_DREAMING_MAX_AGE_DAYS,
|
||||
},
|
||||
deep: {
|
||||
cron: "0 */12 * * *",
|
||||
limit: DEFAULT_MEMORY_DEEP_DREAMING_LIMIT,
|
||||
minScore: 0.8,
|
||||
minRecallCount: 3,
|
||||
minUniqueQueries: 3,
|
||||
recencyHalfLifeDays: DEFAULT_MEMORY_DEEP_DREAMING_RECENCY_HALF_LIFE_DAYS,
|
||||
maxAgeDays: DEFAULT_MEMORY_DEEP_DREAMING_MAX_AGE_DAYS,
|
||||
},
|
||||
rem: {
|
||||
cron: "0 */6 * * *",
|
||||
limit: DEFAULT_MEMORY_DEEP_DREAMING_LIMIT,
|
||||
minScore: 0.85,
|
||||
minRecallCount: 4,
|
||||
minUniqueQueries: 3,
|
||||
recencyHalfLifeDays: DEFAULT_MEMORY_DEEP_DREAMING_RECENCY_HALF_LIFE_DAYS,
|
||||
maxAgeDays: DEFAULT_MEMORY_DEEP_DREAMING_MAX_AGE_DAYS,
|
||||
},
|
||||
};
|
||||
|
||||
function asRecord(value: unknown): Record<string, unknown> | null {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
return null;
|
||||
@@ -288,23 +244,6 @@ function normalizeStringArray<T extends string>(
|
||||
return normalized.length > 0 ? normalized : [...fallback];
|
||||
}
|
||||
|
||||
function parseMemoryDreamingMode(value: unknown): MemoryDreamingMode | undefined {
|
||||
const normalized = normalizeTrimmedString(value)?.toLowerCase();
|
||||
if (
|
||||
normalized === "off" ||
|
||||
normalized === "core" ||
|
||||
normalized === "deep" ||
|
||||
normalized === "rem"
|
||||
) {
|
||||
return normalized;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function normalizeMemoryDreamingMode(value: unknown): MemoryDreamingMode {
|
||||
return parseMemoryDreamingMode(value) ?? DEFAULT_MEMORY_DREAMING_MODE;
|
||||
}
|
||||
|
||||
function normalizeStorageMode(value: unknown): MemoryDreamingStorageMode {
|
||||
const normalized = normalizeTrimmedString(value)?.toLowerCase();
|
||||
if (normalized === "inline" || normalized === "separate" || normalized === "both") {
|
||||
@@ -391,13 +330,8 @@ export function resolveMemoryDreamingConfig(params: {
|
||||
cfg?: OpenClawConfig;
|
||||
}): MemoryDreamingConfig {
|
||||
const dreaming = asRecord(params.pluginConfig?.dreaming);
|
||||
const explicitMode = parseMemoryDreamingMode(dreaming?.mode);
|
||||
const legacyEnabled = normalizeBoolean(dreaming?.enabled, DEFAULT_MEMORY_DREAMING_ENABLED);
|
||||
const mode: MemoryDreamingMode =
|
||||
explicitMode ?? (legacyEnabled ? DEFAULT_MEMORY_DREAMING_PRESET : DEFAULT_MEMORY_DREAMING_MODE);
|
||||
const enabled = mode !== "off";
|
||||
const preset: MemoryDreamingPreset = mode === "off" ? DEFAULT_MEMORY_DREAMING_PRESET : mode;
|
||||
const presetDefaults = MEMORY_DREAMING_PRESET_DEFAULTS[preset];
|
||||
const frequency =
|
||||
normalizeTrimmedString(dreaming?.frequency) ?? DEFAULT_MEMORY_DREAMING_FREQUENCY;
|
||||
const timezone =
|
||||
normalizeTrimmedString(dreaming?.timezone) ??
|
||||
normalizeTrimmedString(params.cfg?.agents?.defaults?.userTimezone) ??
|
||||
@@ -405,7 +339,6 @@ export function resolveMemoryDreamingConfig(params: {
|
||||
const storage = asRecord(dreaming?.storage);
|
||||
const execution = asRecord(dreaming?.execution);
|
||||
const phases = asRecord(dreaming?.phases);
|
||||
const frequencyAlias = normalizeTrimmedString(dreaming?.frequency);
|
||||
|
||||
const defaultExecution = resolveExecutionConfig(execution?.defaults, {
|
||||
speed: DEFAULT_MEMORY_DREAMING_SPEED,
|
||||
@@ -417,39 +350,11 @@ export function resolveMemoryDreamingConfig(params: {
|
||||
const deep = asRecord(phases?.deep);
|
||||
const rem = asRecord(phases?.rem);
|
||||
const deepRecovery = asRecord(deep?.recovery);
|
||||
const maxAgeDays =
|
||||
normalizeOptionalPositiveInt(dreaming?.maxAgeDays) ??
|
||||
normalizeOptionalPositiveInt(deep?.maxAgeDays) ??
|
||||
presetDefaults.maxAgeDays;
|
||||
const deepCron =
|
||||
normalizeTrimmedString(dreaming?.cron) ??
|
||||
frequencyAlias ??
|
||||
normalizeTrimmedString(deep?.cron) ??
|
||||
presetDefaults.cron;
|
||||
const deepLimit = normalizeNonNegativeInt(
|
||||
dreaming?.limit,
|
||||
normalizeNonNegativeInt(deep?.limit, presetDefaults.limit),
|
||||
);
|
||||
const deepMinScore = normalizeScore(
|
||||
dreaming?.minScore,
|
||||
normalizeScore(deep?.minScore, presetDefaults.minScore),
|
||||
);
|
||||
const deepMinRecallCount = normalizeNonNegativeInt(
|
||||
dreaming?.minRecallCount,
|
||||
normalizeNonNegativeInt(deep?.minRecallCount, presetDefaults.minRecallCount),
|
||||
);
|
||||
const deepMinUniqueQueries = normalizeNonNegativeInt(
|
||||
dreaming?.minUniqueQueries,
|
||||
normalizeNonNegativeInt(deep?.minUniqueQueries, presetDefaults.minUniqueQueries),
|
||||
);
|
||||
const deepRecencyHalfLifeDays = normalizeNonNegativeInt(
|
||||
dreaming?.recencyHalfLifeDays,
|
||||
normalizeNonNegativeInt(deep?.recencyHalfLifeDays, presetDefaults.recencyHalfLifeDays),
|
||||
);
|
||||
const maxAgeDays = normalizeOptionalPositiveInt(deep?.maxAgeDays);
|
||||
|
||||
return {
|
||||
mode,
|
||||
enabled,
|
||||
enabled: normalizeBoolean(dreaming?.enabled, DEFAULT_MEMORY_DREAMING_ENABLED),
|
||||
frequency,
|
||||
...(timezone ? { timezone } : {}),
|
||||
verboseLogging: normalizeBoolean(
|
||||
dreaming?.verboseLogging,
|
||||
@@ -467,10 +372,8 @@ export function resolveMemoryDreamingConfig(params: {
|
||||
},
|
||||
phases: {
|
||||
light: {
|
||||
// Phased dreaming was experimental and too noisy. Keep light sleep off
|
||||
// unless we intentionally bring it back behind a separate effort.
|
||||
enabled: false,
|
||||
cron: normalizeTrimmedString(light?.cron) ?? DEFAULT_MEMORY_LIGHT_DREAMING_CRON_EXPR,
|
||||
enabled: normalizeBoolean(light?.enabled, true),
|
||||
cron: frequency,
|
||||
lookbackDays: normalizeNonNegativeInt(
|
||||
light?.lookbackDays,
|
||||
DEFAULT_MEMORY_LIGHT_DREAMING_LOOKBACK_DAYS,
|
||||
@@ -493,14 +396,27 @@ export function resolveMemoryDreamingConfig(params: {
|
||||
}),
|
||||
},
|
||||
deep: {
|
||||
enabled,
|
||||
cron: deepCron,
|
||||
limit: deepLimit,
|
||||
minScore: deepMinScore,
|
||||
minRecallCount: deepMinRecallCount,
|
||||
minUniqueQueries: deepMinUniqueQueries,
|
||||
recencyHalfLifeDays: deepRecencyHalfLifeDays,
|
||||
...(typeof maxAgeDays === "number" ? { maxAgeDays } : {}),
|
||||
enabled: normalizeBoolean(deep?.enabled, true),
|
||||
cron: frequency,
|
||||
limit: normalizeNonNegativeInt(deep?.limit, DEFAULT_MEMORY_DEEP_DREAMING_LIMIT),
|
||||
minScore: normalizeScore(deep?.minScore, DEFAULT_MEMORY_DEEP_DREAMING_MIN_SCORE),
|
||||
minRecallCount: normalizeNonNegativeInt(
|
||||
deep?.minRecallCount,
|
||||
DEFAULT_MEMORY_DEEP_DREAMING_MIN_RECALL_COUNT,
|
||||
),
|
||||
minUniqueQueries: normalizeNonNegativeInt(
|
||||
deep?.minUniqueQueries,
|
||||
DEFAULT_MEMORY_DEEP_DREAMING_MIN_UNIQUE_QUERIES,
|
||||
),
|
||||
recencyHalfLifeDays: normalizeNonNegativeInt(
|
||||
deep?.recencyHalfLifeDays,
|
||||
DEFAULT_MEMORY_DEEP_DREAMING_RECENCY_HALF_LIFE_DAYS,
|
||||
),
|
||||
...(typeof maxAgeDays === "number"
|
||||
? { maxAgeDays }
|
||||
: typeof DEFAULT_MEMORY_DEEP_DREAMING_MAX_AGE_DAYS === "number"
|
||||
? { maxAgeDays: DEFAULT_MEMORY_DEEP_DREAMING_MAX_AGE_DAYS }
|
||||
: {}),
|
||||
sources: normalizeStringArray(
|
||||
deep?.sources,
|
||||
["daily", "memory", "sessions", "logs", "recall"] as const,
|
||||
@@ -540,10 +456,8 @@ export function resolveMemoryDreamingConfig(params: {
|
||||
}),
|
||||
},
|
||||
rem: {
|
||||
// REM reflections are currently disabled by default to keep dreaming
|
||||
// predictable and focused on durable promotion modes.
|
||||
enabled: false,
|
||||
cron: normalizeTrimmedString(rem?.cron) ?? DEFAULT_MEMORY_REM_DREAMING_CRON_EXPR,
|
||||
enabled: normalizeBoolean(rem?.enabled, true),
|
||||
cron: frequency,
|
||||
lookbackDays: normalizeNonNegativeInt(
|
||||
rem?.lookbackDays,
|
||||
DEFAULT_MEMORY_REM_DREAMING_LOOKBACK_DAYS,
|
||||
|
||||
Reference in New Issue
Block a user