fix(doctor): align qmd probe cwd with runtime

This commit is contained in:
Vincent Koc
2026-03-31 19:22:11 +09:00
parent 6b3f99a11f
commit da7f016db6
3 changed files with 15 additions and 1 deletions

View File

@@ -144,6 +144,7 @@ Docs: https://docs.openclaw.ai
- Telegram/audio: transcode Telegram voice-note `.ogg` attachments before the local `whisper-cli` auto fallback runs, and keep mention-preflight transcription enabled in auto mode when `tools.media.audio` is unset.
- Matrix/direct rooms: recover fresh auto-joined 1:1 DMs without eagerly persisting invite-only `m.direct` mappings, while keeping named, aliased, and explicitly configured rooms on the room path. (#58024) Thanks @gumadeiras.
- TTS: Restore 3.28 schema compatibility and fallback observability. (#57953) Thanks @joshavant.
- Memory/doctor: probe QMD availability from the agent workspace too, so `openclaw doctor` no longer falsely reports relative `memory.qmd.command` configs as broken while runtime search still works. Thanks @vincentkoc.
- Telegram/forum topics: restore reply routing to the active topic and keep ACP `sessions_spawn(..., thread=true, mode="session")` bound to that same topic instead of falling back to root chat or losing follow-up routing. (#56060) Thanks @one27001.
- Config/SecretRef + Control UI: harden SecretRef redaction round-trip restore, block unsafe raw fallback (force Form mode when raw is unavailable), and preflight submitted-config SecretRefs before config write RPC persistence. (#58044) Thanks @joshavant.
- Config/Telegram: migrate removed `channels.telegram.groupMentionsOnly` into `channels.telegram.groups["*"].requireMention` on load so legacy configs no longer crash at startup. (#55336) thanks @jameslcowan.

View File

@@ -6,6 +6,7 @@ import type { checkQmdBinaryAvailability as checkQmdBinaryAvailabilityFn } from
const note = vi.hoisted(() => vi.fn());
const resolveDefaultAgentId = vi.hoisted(() => vi.fn(() => "agent-default"));
const resolveAgentDir = vi.hoisted(() => vi.fn(() => "/tmp/agent-default"));
const resolveAgentWorkspaceDir = vi.hoisted(() => vi.fn(() => "/tmp/agent-default/workspace"));
const resolveMemorySearchConfig = vi.hoisted(() => vi.fn());
const resolveApiKeyForProvider = vi.hoisted(() => vi.fn());
const resolveActiveMemoryBackendConfig = vi.hoisted(() => vi.fn());
@@ -21,6 +22,7 @@ vi.mock("../terminal/note.js", () => ({
vi.mock("../agents/agent-scope.js", () => ({
resolveDefaultAgentId,
resolveAgentDir,
resolveAgentWorkspaceDir,
}));
vi.mock("../agents/memory-search.js", () => ({
@@ -62,6 +64,7 @@ describe("noteMemorySearchHealth", () => {
note.mockClear();
resolveDefaultAgentId.mockClear();
resolveAgentDir.mockClear();
resolveAgentWorkspaceDir.mockClear();
resolveMemorySearchConfig.mockReset();
resolveApiKeyForProvider.mockReset();
resolveApiKeyForProvider.mockRejectedValue(new Error("missing key"));
@@ -141,6 +144,11 @@ describe("noteMemorySearchHealth", () => {
await noteMemorySearchHealth(cfg, {});
expect(note).not.toHaveBeenCalled();
expect(checkQmdBinaryAvailability).toHaveBeenCalledWith({
command: "qmd",
env: process.env,
cwd: "/tmp/agent-default/workspace",
});
});
it("warns when QMD backend is active but the qmd binary is unavailable", async () => {

View File

@@ -1,5 +1,9 @@
import fsSync from "node:fs";
import { resolveAgentDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import {
resolveAgentDir,
resolveAgentWorkspaceDir,
resolveDefaultAgentId,
} from "../agents/agent-scope.js";
import { resolveMemorySearchConfig } from "../agents/memory-search.js";
import { resolveApiKeyForProvider } from "../agents/model-auth.js";
import { formatCliCommand } from "../cli/command-format.js";
@@ -58,6 +62,7 @@ export async function noteMemorySearchHealth(
const qmdCheck = await checkQmdBinaryAvailability({
command: backendConfig.qmd?.command ?? "qmd",
env: process.env,
cwd: resolveAgentWorkspaceDir(cfg, agentId),
});
if (!qmdCheck.available) {
note(