Skip to content

Commit 7c9108a

Browse files
committed
fix(memory-qmd): streamline compatibility coverage
1 parent f6dbcf4 commit 7c9108a

7 files changed

Lines changed: 25 additions & 48 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ Docs: https://docs.openclaw.ai
127127
- Discord: keep REST, webhook, and monitor traffic on the configured proxy, preserve component-only media sends, honor `@everyone` and `@here` mention gates, keep ACK reactions on the active account, and split voice connect/playback timeouts so auto-join is more reliable. (#57465, #60361, #60345) Thanks @geekhuashan.
128128
- WhatsApp: restore `channels.whatsapp.blockStreaming` and reset watchdog timeouts after reconnect so quiet chats stop falling into reconnect loops. (#60007, #60069) Thanks @MonkeyLeeT and @mcaxtr.
129129
- Memory: keep `memory-core` builtin embedding registration on the already-registered path so selecting `memory-core` no longer recurses through plugin discovery and crashes during startup. (#61402) Thanks @ngutman.
130-
- Memory/QMD: prefer modern `qmd collection add --glob`, accept newer single-line JSON hit metadata while keeping legacy line fields, and refresh QMD docs/model-override guidance without breaking older QMD releases. Thanks @vincentkoc.
130+
- Memory/QMD: prefer modern `qmd collection add --glob`, accept newer single-line JSON hit metadata while keeping legacy line fields, refresh QMD docs/doctor install guidance and model-override guidance, and keep older QMD releases working. Thanks @vincentkoc.
131131
- MS Teams: download inline DM images via Graph API and preserve channel reply threading in proactive fallback. (#52212, #55198) Thanks @Ted-developer and @hyojin.
132132
- MS Teams: replace the deprecated Teams SDK HttpPlugin stub with `httpServerAdapter` so recurring gateway deprecation warnings stop firing and the Express 5 compatibility workaround stays on the supported SDK path. (#60939) Thanks @coolramukaka-sys.
133133
- Matrix/exec approvals: anchor seeded approval reactions to the primary Matrix prompt event, resolve them from event metadata instead of prompt text, and clean up chunked approval prompts correctly. (#60931) Thanks @gumadeiras.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { describe, expect, it } from "vitest";
2+
import { resolveQmdCollectionPatternFlags } from "./qmd-compat.js";
3+
4+
describe("resolveQmdCollectionPatternFlags", () => {
5+
it("prefers modern --glob by default and falls back to legacy --mask", () => {
6+
expect(resolveQmdCollectionPatternFlags(null)).toEqual(["--glob", "--mask"]);
7+
expect(resolveQmdCollectionPatternFlags("--glob")).toEqual(["--glob", "--mask"]);
8+
});
9+
10+
it("keeps preferring legacy --mask after a legacy-only qmd succeeds", () => {
11+
expect(resolveQmdCollectionPatternFlags("--mask")).toEqual(["--mask", "--glob"]);
12+
});
13+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export type QmdCollectionPatternFlag = "--glob" | "--mask";
2+
3+
export function resolveQmdCollectionPatternFlags(
4+
preferredFlag: QmdCollectionPatternFlag | null,
5+
): QmdCollectionPatternFlag[] {
6+
return preferredFlag === "--mask" ? ["--mask", "--glob"] : ["--glob", "--mask"];
7+
}

extensions/memory-core/src/memory/qmd-manager.test.ts

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -895,49 +895,6 @@ describe("QmdMemoryManager", () => {
895895
);
896896
});
897897

898-
it("prefers --glob for collection add and falls back to --mask when --glob is rejected", async () => {
899-
cfg = {
900-
...cfg,
901-
memory: {
902-
backend: "qmd",
903-
qmd: {
904-
includeDefaultMemory: true,
905-
update: { interval: "0s", debounceMs: 60_000, onBoot: false },
906-
paths: [],
907-
},
908-
},
909-
} as OpenClawConfig;
910-
911-
const addFlagCalls: string[] = [];
912-
spawnMock.mockImplementation((_cmd: string, args: string[]) => {
913-
if (args[0] === "collection" && args[1] === "list") {
914-
const child = createMockChild({ autoClose: false });
915-
emitAndClose(child, "stdout", "[]");
916-
return child;
917-
}
918-
if (args[0] === "collection" && args[1] === "add") {
919-
const child = createMockChild({ autoClose: false });
920-
const flag = args.includes("--glob") ? "--glob" : args.includes("--mask") ? "--mask" : "";
921-
addFlagCalls.push(flag);
922-
if (flag === "--glob") {
923-
emitAndClose(child, "stderr", "unknown flag: --glob", 1);
924-
return child;
925-
}
926-
queueMicrotask(() => child.closeWith(0));
927-
return child;
928-
}
929-
return createMockChild();
930-
});
931-
932-
const { manager } = await createManager({ mode: "full" });
933-
await manager.close();
934-
935-
expect(addFlagCalls).toEqual(["--glob", "--mask", "--mask", "--mask"]);
936-
expect(logWarnMock).toHaveBeenCalledWith(
937-
expect.stringContaining("retrying with legacy compatibility flag"),
938-
);
939-
});
940-
941898
it("migrates unscoped legacy collections from plain-text collection list output", async () => {
942899
cfg = {
943900
...cfg,

extensions/memory-core/src/memory/qmd-manager.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
type ResolvedQmdConfig,
4242
type ResolvedQmdMcporterConfig,
4343
} from "openclaw/plugin-sdk/memory-core-host-engine-storage";
44+
import { resolveQmdCollectionPatternFlags, type QmdCollectionPatternFlag } from "./qmd-compat.js";
4445

4546
type SqliteDatabase = import("node:sqlite").DatabaseSync;
4647

@@ -165,7 +166,6 @@ type ManagedCollection = {
165166
};
166167

167168
type QmdManagerMode = "full" | "status";
168-
type QmdCollectionPatternFlag = "--glob" | "--mask";
169169
type BuiltinQmdMcpTool = "query" | "search" | "vector_search" | "deep_search";
170170
type QmdMcporterSearchParams =
171171
| {
@@ -652,8 +652,7 @@ export class QmdMemoryManager implements MemorySearchManager {
652652
}
653653

654654
private async addCollection(pathArg: string, name: string, pattern: string): Promise<void> {
655-
const candidateFlags: QmdCollectionPatternFlag[] =
656-
this.collectionPatternFlag === "--glob" ? ["--glob", "--mask"] : ["--mask", "--glob"];
655+
const candidateFlags = resolveQmdCollectionPatternFlags(this.collectionPatternFlag);
657656
let lastError: unknown;
658657
for (const flag of candidateFlags) {
659658
try {

src/commands/doctor-memory-search.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ describe("noteMemorySearchHealth", () => {
232232
expect(message).toContain("QMD memory backend is configured");
233233
expect(message).toContain("spawn qmd ENOENT");
234234
expect(message).toContain("npm install -g @tobilu/qmd");
235+
expect(message).toContain("bun install -g @tobilu/qmd");
235236
});
236237

237238
it("does not warn when remote apiKey is configured for explicit provider", async () => {

src/commands/doctor-memory-search.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ export async function noteMemorySearchHealth(
218218
qmdCheck.error ? `Probe error: ${qmdCheck.error}` : null,
219219
"",
220220
"Fix (pick one):",
221-
"- Install the supported QMD package: npm install -g @tobilu/qmd",
221+
"- Install the supported QMD package: npm install -g @tobilu/qmd (or bun install -g @tobilu/qmd)",
222222
`- Set an explicit binary path: ${formatCliCommand("openclaw config set memory.qmd.command /absolute/path/to/qmd")}`,
223223
`- Or switch back to builtin memory: ${formatCliCommand("openclaw config set memory.backend builtin")}`,
224224
"",

0 commit comments

Comments
 (0)