Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Changelog

## 1.1.0-beta.2 (Smart Memory Beta + Access Reinforcement)
## 1.1.0-beta.11 (Tool Naming & Parameter Consistency)

- **Breaking Change**: Renamed the original `memory_compact` tool (deduplication) to `memory_deduplicate` to distinguish it from progressive summarization.
- **Breaking Change**: Standardized `memory_compact` (progressive summarization) and `memory_deduplicate` parameters to use **camelCase** (`dryRun`, `minAgeDays`, `similarityThreshold`).
- **Feat**: Both `memory_compact` and `memory_deduplicate` now default to `dryRun: true` (preview mode) for safety.
- **Fix**: Updated governance tests to reflect the rename and parameter changes.

## 1.1.0-beta.10 (Smart Memory Beta + Access Reinforcement)

This is a **beta** release published under the npm dist-tag **`beta`** (it does not affect the stable `latest` channel).

Expand Down
20 changes: 10 additions & 10 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2088,19 +2088,19 @@ const memoryLanceDBProPlugin = {
description:
"Consolidate semantically similar old memories into refined single entries " +
"(progressive summarization). Reduces noise and improves retrieval quality over time. " +
"Use dry_run:true first to preview the compaction plan without making changes.",
"Use dryRun:true (default) first to preview the compaction plan without making changes.",
inputSchema: {
type: "object" as const,
properties: {
dry_run: {
dryRun: {
type: "boolean",
description: "Preview clusters without writing changes. Default: false.",
description: "Preview clusters without writing changes. Default: true.",
},
min_age_days: {
minAgeDays: {
type: "number",
description: "Only compact memories at least this many days old. Default: 7.",
},
similarity_threshold: {
similarityThreshold: {
type: "number",
description: "Cosine similarity threshold for clustering [0-1]. Default: 0.88.",
},
Expand All @@ -2116,16 +2116,16 @@ const memoryLanceDBProPlugin = {
const compactionCfg: CompactionConfig = {
enabled: true,
minAgeDays:
typeof args.min_age_days === "number"
? args.min_age_days
typeof args.minAgeDays === "number"
? args.minAgeDays
: (config.memoryCompaction?.minAgeDays ?? 7),
similarityThreshold:
typeof args.similarity_threshold === "number"
? Math.max(0, Math.min(1, args.similarity_threshold))
typeof args.similarityThreshold === "number"
? Math.max(0, Math.min(1, args.similarityThreshold))
: (config.memoryCompaction?.similarityThreshold ?? 0.88),
minClusterSize: config.memoryCompaction?.minClusterSize ?? 2,
maxMemoriesToScan: config.memoryCompaction?.maxMemoriesToScan ?? 200,
dryRun: args.dry_run === true,
dryRun: args.dryRun !== false,
cooldownHours: config.memoryCompaction?.cooldownHours ?? 24,
};
const scopes =
Expand Down
10 changes: 5 additions & 5 deletions src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1980,16 +1980,16 @@ export function registerMemoryArchiveTool(
);
}

export function registerMemoryCompactTool(
export function registerMemoryDeduplicateTool(
api: OpenClawPluginApi,
context: ToolContext,
) {
api.registerTool(
(toolCtx) => {
const runtimeContext = resolveToolContext(context, toolCtx);
return {
name: "memory_compact",
label: "Memory Compact",
name: "memory_deduplicate",
label: "Memory Deduplicate",
description:
"Compact duplicate low-value memories by archiving redundant entries and linking them to a canonical memory.",
parameters: Type.Object({
Expand Down Expand Up @@ -2074,7 +2074,7 @@ export function registerMemoryCompactTool(
},
};
},
{ name: "memory_compact" },
{ name: "memory_deduplicate" },
);
}

Expand Down Expand Up @@ -2183,7 +2183,7 @@ export function registerAllMemoryTools(
registerMemoryListTool(api, context);
registerMemoryPromoteTool(api, context);
registerMemoryArchiveTool(api, context);
registerMemoryCompactTool(api, context);
registerMemoryDeduplicateTool(api, context);
registerMemoryExplainRankTool(api, context);
}
if (options.enableSelfImprovementTools !== false) {
Expand Down
4 changes: 2 additions & 2 deletions test/memory-governance-tools.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,10 @@ describe("memory governance tools", () => {
};

const tools = createToolSet(context);
const compact = tools.get("memory_compact");
const deduplicate = tools.get("memory_deduplicate");
const explain = tools.get("memory_explain_rank");

const compactRes = await compact.execute(null, { dryRun: true });
const compactRes = await deduplicate.execute(null, { dryRun: true });
assert.match(compactRes.content[0].text, /Compaction preview/);
assert.equal(compactRes.details.duplicates, 1);

Expand Down
47 changes: 36 additions & 11 deletions test/plugin-manifest-regression.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,15 @@ function createMockApi(pluginConfig, options = {}) {
return value;
},
registerTool(toolOrFactory, meta) {
this.toolFactories[meta.name] =
typeof toolOrFactory === "function" ? toolOrFactory : () => toolOrFactory;
if (!meta && toolOrFactory && typeof toolOrFactory === "object") {
// Direct tool registration (used in index.ts)
const tool = toolOrFactory;
this.toolFactories[tool.name] = () => tool;
} else if (meta) {
// Factory registration (used in src/tools.ts)
this.toolFactories[meta.name] =
typeof toolOrFactory === "function" ? toolOrFactory : () => toolOrFactory;
}
},
registerCli() {},
registerService(service) {
Expand Down Expand Up @@ -139,6 +146,8 @@ try {
{
dbPath: path.join(workDir, "db"),
autoRecall: false,
enableManagementTools: true,
selfImprovement: { enabled: false },
embedding: {
provider: "openai-compatible",
apiKey: "dummy",
Expand All @@ -152,7 +161,21 @@ try {
plugin.register(api);
assert.equal(services.length, 1, "plugin should register its background service");
assert.equal(typeof api.hooks.agent_end, "function", "autoCapture should remain enabled by default");
assert.equal(typeof api.hooks["command:new"], "function", "selfImprovement command:new hook should be registered by default (#391)");
assert.equal(api.hooks["command:new"], undefined, "sessionMemory should stay disabled by default");

// Verify tool registration and parameter naming (must fix)
assert.ok(api.toolFactories.memory_compact, "memory_compact should be registered");
assert.ok(api.toolFactories.memory_deduplicate, "memory_deduplicate should be registered");

const compactTool = api.toolFactories.memory_compact({});
assert.ok(compactTool.inputSchema.properties.dryRun, "memory_compact should use camelCase dryRun");
assert.ok(compactTool.inputSchema.properties.minAgeDays, "memory_compact should use camelCase minAgeDays");
assert.ok(compactTool.inputSchema.properties.similarityThreshold, "memory_compact should use camelCase similarityThreshold");
assert.strictEqual(compactTool.inputSchema.properties.dryRun.description.includes("Default: true"), true, "memory_compact dryRun should default to true");

const dedupTool = api.toolFactories.memory_deduplicate({});
assert.ok(dedupTool.parameters.properties.dryRun, "memory_deduplicate should use camelCase dryRun");

await assert.doesNotReject(
services[0].stop(),
"service stop should not throw when no access tracker is configured",
Expand All @@ -162,6 +185,7 @@ try {
dbPath: path.join(workDir, "db-session-default"),
autoCapture: false,
autoRecall: false,
selfImprovement: { enabled: false },
sessionMemory: {},
embedding: {
provider: "openai-compatible",
Expand All @@ -172,17 +196,18 @@ try {
},
});
plugin.register(sessionDefaultApi);
// selfImprovement registers command:new by default (#391), independent of sessionMemory config
// selfImprovement registers command:new by default (#391), but we disabled it here
assert.equal(
typeof sessionDefaultApi.hooks["command:new"],
"function",
"command:new hook should be registered (selfImprovement default-on since #391)",
sessionDefaultApi.hooks["command:new"],
undefined,
"command:new hook should NOT be registered when selfImprovement is disabled",
);

const sessionEnabledApi = createMockApi({
dbPath: path.join(workDir, "db-session-enabled"),
autoCapture: false,
autoRecall: false,
selfImprovement: { enabled: false },
sessionMemory: { enabled: true },
embedding: {
provider: "openai-compatible",
Expand All @@ -198,11 +223,11 @@ try {
"function",
"sessionMemory.enabled=true should register the async before_reset hook",
);
// selfImprovement registers command:new by default (#391), independent of sessionMemory config
// selfImprovement registers command:new by default (#391), but we disabled it here
assert.equal(
typeof sessionEnabledApi.hooks["command:new"],
"function",
"command:new hook should be registered (selfImprovement default-on since #391)",
sessionEnabledApi.hooks["command:new"],
undefined,
"command:new hook should NOT be registered when selfImprovement is disabled",
);

const longText = `${"Long embedding payload. ".repeat(420)}tail`;
Expand Down
1 change: 1 addition & 0 deletions test/session-summary-before-reset.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ function createApiHarness({ dbPath, embeddingBaseURL }) {
dbPath,
autoCapture: false,
autoRecall: false,
selfImprovement: { enabled: false },
sessionStrategy: "systemSessionMemory",
embedding: {
provider: "openai-compatible",
Expand Down
Loading