diff --git a/CHANGELOG.md b/CHANGELOG.md index ff3a805bf69..0c5bfcdc420 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -128,6 +128,7 @@ Docs: https://docs.openclaw.ai - Agents/Compaction safeguard structure hardening: require exact fallback summary headings, sanitize untrusted compaction instruction text before prompt embedding, and keep structured sections when preserving all turns. (#25555) thanks @rodrigouroz. - Gateway/status self version reporting: make Gateway self version in `openclaw status` prefer runtime `VERSION` (while preserving explicit `OPENCLAW_VERSION` override), preventing stale post-upgrade app version output. (#32655) thanks @liuxiaopai-ai. - Memory/QMD index isolation: set `QMD_CONFIG_DIR` alongside `XDG_CONFIG_HOME` so QMD config state stays per-agent despite upstream XDG handling bugs, preventing cross-agent collection indexing and excess disk/CPU usage. (#27028) thanks @HenryLoenwind. +- Memory/QMD collection safety: stop destructive collection rebinds when QMD `collection list` only reports names without path metadata, preventing `memory search` from dropping existing collections if re-add fails. (#36870) Thanks @Adnannnnnnna. - Memory/local embedding initialization hardening: add regression coverage for transient initialization retry and mixed `embedQuery` + `embedBatch` concurrent startup to lock single-flight initialization behavior. (#15639) thanks @SubtleSpark. - CLI/Coding-agent reliability: switch default `claude-cli` non-interactive args to `--permission-mode bypassPermissions`, auto-normalize legacy `--dangerously-skip-permissions` backend overrides to the modern permission-mode form, align coding-agent + live-test docs with the non-PTY Claude path, and emit session system-event heartbeat notices when CLI watchdog no-output timeouts terminate runs. Related to #28261. Landed from contributor PRs #28610 and #31149. Thanks @niceysam, @cryptomaltese and @vincentkoc. - ACP/ACPX session bootstrap: retry with `sessions new` when `sessions ensure` returns no session identifiers so ACP spawns avoid `NO_SESSION`/`ACP_TURN_FAILED` failures on affected agents. Related to #28786. Landed from contributor PR #31338. Thanks @Sid-Qin and @vincentkoc. diff --git a/src/memory/qmd-manager.test.ts b/src/memory/qmd-manager.test.ts index 0532dd6099e..43f7c55be50 100644 --- a/src/memory/qmd-manager.test.ts +++ b/src/memory/qmd-manager.test.ts @@ -369,7 +369,7 @@ describe("QmdMemoryManager", () => { expect(addSessions?.[2]).toBe(path.join(stateDir, "agents", devAgentId, "qmd", "sessions")); }); - it("rebinds managed collections when qmd only reports collection names", async () => { + it("avoids destructive rebind when qmd only reports collection names", async () => { cfg = { ...cfg, memory: { @@ -401,25 +401,11 @@ describe("QmdMemoryManager", () => { await manager.close(); const commands = spawnMock.mock.calls.map((call: unknown[]) => call[1] as string[]); - const removeSessions = commands.find( - (args) => - args[0] === "collection" && args[1] === "remove" && args[2] === sessionCollectionName, - ); - expect(removeSessions).toBeDefined(); - const removeWorkspace = commands.find( - (args) => - args[0] === "collection" && args[1] === "remove" && args[2] === `workspace-${agentId}`, - ); - expect(removeWorkspace).toBeDefined(); + const removeCalls = commands.filter((args) => args[0] === "collection" && args[1] === "remove"); + expect(removeCalls).toHaveLength(0); - const addSessions = commands.find((args) => { - if (args[0] !== "collection" || args[1] !== "add") { - return false; - } - const nameIdx = args.indexOf("--name"); - return nameIdx >= 0 && args[nameIdx + 1] === sessionCollectionName; - }); - expect(addSessions).toBeDefined(); + const addCalls = commands.filter((args) => args[0] === "collection" && args[1] === "add"); + expect(addCalls).toHaveLength(0); }); it("migrates unscoped legacy collections before adding scoped names", async () => { diff --git a/src/memory/qmd-manager.ts b/src/memory/qmd-manager.ts index 789b88f6f6c..454cad6833a 100644 --- a/src/memory/qmd-manager.ts +++ b/src/memory/qmd-manager.ts @@ -568,8 +568,9 @@ export class QmdMemoryManager implements MemorySearchManager { private shouldRebindCollection(collection: ManagedCollection, listed: ListedCollection): boolean { if (!listed.path) { // Older qmd versions may only return names from `collection list --json`. - // Rebind managed collections so stale path bindings cannot survive upgrades. - return true; + // Do not perform destructive rebinds when metadata is incomplete: remove+add + // can permanently drop collections if add fails (for example on timeout). + return false; } if (!this.pathsMatch(listed.path, collection.path)) { return true;