diff --git a/CHANGELOG.md b/CHANGELOG.md index 28c02b56..49dc350b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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). diff --git a/index.ts b/index.ts index 7bc9d21e..18a92606 100644 --- a/index.ts +++ b/index.ts @@ -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.", }, @@ -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 = diff --git a/src/tools.ts b/src/tools.ts index 6551fda1..87697bc7 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -1980,7 +1980,7 @@ export function registerMemoryArchiveTool( ); } -export function registerMemoryCompactTool( +export function registerMemoryDeduplicateTool( api: OpenClawPluginApi, context: ToolContext, ) { @@ -1988,8 +1988,8 @@ export function registerMemoryCompactTool( (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({ @@ -2074,7 +2074,7 @@ export function registerMemoryCompactTool( }, }; }, - { name: "memory_compact" }, + { name: "memory_deduplicate" }, ); } @@ -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) { diff --git a/test/memory-governance-tools.test.mjs b/test/memory-governance-tools.test.mjs index a1ed0667..8b526c1d 100644 --- a/test/memory-governance-tools.test.mjs +++ b/test/memory-governance-tools.test.mjs @@ -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); diff --git a/test/plugin-manifest-regression.mjs b/test/plugin-manifest-regression.mjs index cc5232bd..f7c5eab0 100644 --- a/test/plugin-manifest-regression.mjs +++ b/test/plugin-manifest-regression.mjs @@ -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) { @@ -139,6 +146,8 @@ try { { dbPath: path.join(workDir, "db"), autoRecall: false, + enableManagementTools: true, + selfImprovement: { enabled: false }, embedding: { provider: "openai-compatible", apiKey: "dummy", @@ -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", @@ -162,6 +185,7 @@ try { dbPath: path.join(workDir, "db-session-default"), autoCapture: false, autoRecall: false, + selfImprovement: { enabled: false }, sessionMemory: {}, embedding: { provider: "openai-compatible", @@ -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", @@ -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`; diff --git a/test/session-summary-before-reset.test.mjs b/test/session-summary-before-reset.test.mjs index 1432160d..01b26a29 100644 --- a/test/session-summary-before-reset.test.mjs +++ b/test/session-summary-before-reset.test.mjs @@ -44,6 +44,7 @@ function createApiHarness({ dbPath, embeddingBaseURL }) { dbPath, autoCapture: false, autoRecall: false, + selfImprovement: { enabled: false }, sessionStrategy: "systemSessionMemory", embedding: { provider: "openai-compatible",