diff --git a/CHANGELOG.md b/CHANGELOG.md index 76cde82eed7..02166cfa4b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Docs: https://docs.openclaw.ai - Memory/QMD: parse qmd scope keys once per request to avoid repeated parsing in scope checks. - Memory/QMD: query QMD index using exact docid matches before falling back to prefix lookup for better recall correctness and index efficiency. - Memory/QMD: make QMD result JSON parsing resilient to noisy command output by extracting the first JSON array from noisy `stdout`. +- Memory/QMD: treat prefixed `no results found` marker output as an empty result set in qmd JSON parsing. (#11302) Thanks @blazerui. - Memory/QMD: pass result limits to `search`/`vsearch` commands so QMD can cap results earlier. - Memory/QMD: avoid reading full markdown files when a `from/lines` window is requested in QMD reads. - Memory/QMD: skip rewriting unchanged session export markdown files during sync to reduce disk churn. diff --git a/src/memory/qmd-query-parser.test.ts b/src/memory/qmd-query-parser.test.ts index ed94b18e957..c4d402812a9 100644 --- a/src/memory/qmd-query-parser.test.ts +++ b/src/memory/qmd-query-parser.test.ts @@ -29,6 +29,17 @@ complete`, expect(results).toEqual([]); }); + it("treats prefixed no-results marker output as an empty result set", () => { + expect(parseQmdQueryJson("warning: no results found", "")).toEqual([]); + expect(parseQmdQueryJson("", "[qmd] warning: no results found\n")).toEqual([]); + }); + + it("does not treat arbitrary non-marker text as no-results output", () => { + expect(() => + parseQmdQueryJson("warning: search completed; no results found for this query", ""), + ).toThrow(/qmd query returned invalid JSON/i); + }); + it("throws when stdout cannot be interpreted as qmd JSON", () => { expect(() => parseQmdQueryJson("this is not json", "")).toThrow( /qmd query returned invalid JSON/i, diff --git a/src/memory/qmd-query-parser.ts b/src/memory/qmd-query-parser.ts index 9aae1e9c0b3..a049527738a 100644 --- a/src/memory/qmd-query-parser.ts +++ b/src/memory/qmd-query-parser.ts @@ -46,8 +46,20 @@ export function parseQmdQueryJson(stdout: string, stderr: string): QmdQueryResul } function isQmdNoResultsOutput(raw: string): boolean { - const normalized = raw.trim().toLowerCase().replace(/\s+/g, " "); - return normalized === "no results found" || normalized === "no results found."; + const lines = raw + .split(/\r?\n/) + .map((line) => line.trim().toLowerCase().replace(/\s+/g, " ")) + .filter((line) => line.length > 0); + return lines.some((line) => isQmdNoResultsLine(line)); +} + +function isQmdNoResultsLine(line: string): boolean { + if (line === "no results found" || line === "no results found.") { + return true; + } + return /^(?:\[[^\]]+\]\s*)?(?:(?:warn(?:ing)?|info|error|qmd)\s*:\s*)+no results found\.?$/.test( + line, + ); } function summarizeQmdStderr(raw: string): string {