feat(memory): enable smart memory defaults for all new installations
Adds applyMemorySearchDefaults() to the config pipeline, enabling three platform-level features that together solve the multi-session continuity problem (the 'twin sessions' problem where webchat and Telegram feel like separate assistants with no shared memory). Previously, users had to manually configure memorySearch in openclaw.json to get these features. New installs now get them automatically. ## What this enables by default 1. **Session memory indexing** (experimental.sessionMemory + sources: sessions) Every conversation across all channels is automatically indexed. A memory_search from any channel can find context from any other channel without the user or agent needing to manually write memory files. 2. **Temporal decay** (halfLifeDays: 30) Recent memories rank higher automatically. Yesterday's note beats a 6-month-old note on the same topic. 3. **MMR re-ranking** (lambda: 0.7) Avoids returning near-duplicate memory snippets. The agent gets diverse, complementary results instead of the same information repeated. ## Design decisions - Opt-out not opt-in: applied only when memorySearch is undefined. - Platform layer not agent instructions: lives in code, not prose. - Conservative values matching existing docs recommendations. - Same pipeline pattern as applyContextPruningDefaults / applyCompactionDefaults. ## Testing Added config.memory-search-defaults.test.ts: - Default install gets all three features enabled - Explicit memorySearch config is never overwritten
This commit is contained in:
parent
e78129a4d9
commit
bf282b5055
52
src/config/config.memory-search-defaults.test.ts
Normal file
52
src/config/config.memory-search-defaults.test.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { loadConfig } from "./config.js";
|
||||
import { withTempHomeConfig } from "./test-helpers.js";
|
||||
|
||||
describe("config memory search defaults", () => {
|
||||
it("enables session memory indexing, temporal decay, and MMR by default", async () => {
|
||||
await withTempHomeConfig({}, async () => {
|
||||
const cfg = loadConfig();
|
||||
const ms = cfg.agents?.defaults?.memorySearch;
|
||||
|
||||
// Session memory indexing should be on
|
||||
expect((ms?.experimental as Record<string, unknown>)?.sessionMemory).toBe(true);
|
||||
expect(ms?.sources).toContain("sessions");
|
||||
expect(ms?.sources).toContain("memory");
|
||||
|
||||
// Hybrid search with temporal decay
|
||||
const hybrid = ms?.query?.hybrid;
|
||||
expect(hybrid?.enabled).toBe(true);
|
||||
expect(hybrid?.temporalDecay?.enabled).toBe(true);
|
||||
expect(hybrid?.temporalDecay?.halfLifeDays).toBe(30);
|
||||
|
||||
// MMR re-ranking
|
||||
expect(hybrid?.mmr?.enabled).toBe(true);
|
||||
expect(hybrid?.mmr?.lambda).toBe(0.7);
|
||||
});
|
||||
});
|
||||
|
||||
it("does not overwrite explicit memorySearch config", async () => {
|
||||
await withTempHomeConfig(
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
sources: ["memory"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async () => {
|
||||
const cfg = loadConfig();
|
||||
const ms = cfg.agents?.defaults?.memorySearch;
|
||||
|
||||
// User explicitly set sources — respect it, don't inject sessions
|
||||
expect(ms?.sources).toEqual(["memory"]);
|
||||
expect(ms?.sources).not.toContain("sessions");
|
||||
|
||||
// No defaults injected since user provided explicit config
|
||||
expect((ms?.experimental as Record<string, unknown>)?.sessionMemory).toBeUndefined();
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -531,6 +531,68 @@ export function applyCompactionDefaults(cfg: OpenClawConfig): OpenClawConfig {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies smart memory search defaults for new installations.
|
||||
*
|
||||
* Enables three features that work together to solve the multi-session
|
||||
* continuity problem (e.g. webchat and Telegram feeling like "two twins
|
||||
* who don't share memory") without requiring any agent-level instructions:
|
||||
*
|
||||
* 1. **Session memory indexing** – every conversation across all channels is
|
||||
* automatically indexed so `memory_search` can find context from any session.
|
||||
*
|
||||
* 2. **Temporal decay** – recent memories rank higher automatically; a note
|
||||
* from yesterday beats one from 6 months ago on the same topic.
|
||||
*
|
||||
* 3. **MMR re-ranking** – avoids returning near-duplicate memory snippets;
|
||||
* the agent gets diverse, complementary results instead of repetition.
|
||||
*
|
||||
* All three are off by default today. This function enables them for users
|
||||
* who haven't explicitly configured `memorySearch`, so new installations
|
||||
* get the best out-of-the-box experience. Existing explicit config is
|
||||
* never overwritten (opt-out by setting any memorySearch field).
|
||||
*/
|
||||
export function applyMemorySearchDefaults(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const defaults = cfg.agents?.defaults;
|
||||
if (!defaults) {
|
||||
return cfg;
|
||||
}
|
||||
|
||||
// Don't touch anything if the user has explicitly configured memorySearch
|
||||
if (defaults.memorySearch !== undefined) {
|
||||
return cfg;
|
||||
}
|
||||
|
||||
return {
|
||||
...cfg,
|
||||
agents: {
|
||||
...cfg.agents,
|
||||
defaults: {
|
||||
...defaults,
|
||||
memorySearch: {
|
||||
experimental: { sessionMemory: true },
|
||||
sources: ["memory", "sessions"],
|
||||
query: {
|
||||
hybrid: {
|
||||
enabled: true,
|
||||
vectorWeight: 0.7,
|
||||
textWeight: 0.3,
|
||||
temporalDecay: {
|
||||
enabled: true,
|
||||
halfLifeDays: 30,
|
||||
},
|
||||
mmr: {
|
||||
enabled: true,
|
||||
lambda: 0.7,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function resetSessionDefaultsWarningForTests() {
|
||||
defaultWarnState = { warned: false };
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ import { maintainConfigBackups } from "./backup-rotation.js";
|
||||
import {
|
||||
applyCompactionDefaults,
|
||||
applyContextPruningDefaults,
|
||||
applyMemorySearchDefaults,
|
||||
applyAgentDefaults,
|
||||
applyLoggingDefaults,
|
||||
applyMessageDefaults,
|
||||
@ -800,8 +801,12 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
|
||||
applyModelDefaults(
|
||||
applyCompactionDefaults(
|
||||
applyContextPruningDefaults(
|
||||
applyAgentDefaults(
|
||||
applySessionDefaults(applyLoggingDefaults(applyMessageDefaults(validated.config))),
|
||||
applyMemorySearchDefaults(
|
||||
applyAgentDefaults(
|
||||
applySessionDefaults(
|
||||
applyLoggingDefaults(applyMessageDefaults(validated.config)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -892,7 +897,9 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
|
||||
applyModelDefaults(
|
||||
applyCompactionDefaults(
|
||||
applyContextPruningDefaults(
|
||||
applyAgentDefaults(applySessionDefaults(applyMessageDefaults({}))),
|
||||
applyMemorySearchDefaults(
|
||||
applyAgentDefaults(applySessionDefaults(applyMessageDefaults({}))),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user