From 8a5cb349946ed971ece71fd9817140ba9812b46a Mon Sep 17 00:00:00 2001 From: SidQin-cyber Date: Wed, 25 Feb 2026 09:33:45 +0800 Subject: [PATCH 1/3] fix(cli): support --query flag for memory search Accept `openclaw memory search --query "..."` as a documented alias for positional query input so CLI behavior matches its own help examples and avoids unknown-option failures. Co-authored-by: Cursor --- src/cli/memory-cli.test.ts | 16 ++++++++++++++++ src/cli/memory-cli.ts | 16 +++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/cli/memory-cli.test.ts b/src/cli/memory-cli.test.ts index 8a83bc5e906c2..6a0dc2607bb30 100644 --- a/src/cli/memory-cli.test.ts +++ b/src/cli/memory-cli.test.ts @@ -382,6 +382,22 @@ describe("memory cli", () => { expect(close).toHaveBeenCalled(); }); + it("accepts --query flag for memory search", async () => { + const close = vi.fn(async () => {}); + const search = vi.fn(async () => []); + mockManager({ search, close }); + + const log = spyRuntimeLogs(); + await runMemoryCli(["search", "--query", "deployment notes"]); + + expect(search).toHaveBeenCalledWith("deployment notes", { + maxResults: undefined, + minScore: undefined, + }); + expect(log).toHaveBeenCalledWith("No matches."); + expect(close).toHaveBeenCalled(); + }); + it("prints search results as json when requested", async () => { const close = vi.fn(async () => {}); const search = vi.fn(async () => [ diff --git a/src/cli/memory-cli.ts b/src/cli/memory-cli.ts index 6449653f8ac22..cf457368e3ba7 100644 --- a/src/cli/memory-cli.ts +++ b/src/cli/memory-cli.ts @@ -702,19 +702,29 @@ export function registerMemoryCli(program: Command) { memory .command("search") .description("Search memory files") - .argument("", "Search query") + .argument("[query]", "Search query") + .option("--query ", "Search query (flag alias)") .option("--agent ", "Agent id (default: default agent)") .option("--max-results ", "Max results", (value: string) => Number(value)) .option("--min-score ", "Minimum score", (value: string) => Number(value)) .option("--json", "Print JSON") .action( async ( - query: string, + queryArg: string | undefined, opts: MemoryCommandOptions & { + query?: string; maxResults?: number; minScore?: number; }, ) => { + const resolvedQuery = (opts.query ?? queryArg)?.trim() ?? ""; + if (!resolvedQuery) { + defaultRuntime.error( + 'Missing search query. Use `openclaw memory search ""` or `--query ""`.', + ); + process.exitCode = 1; + return; + } const cfg = loadConfig(); const agentId = resolveAgent(cfg, opts.agent); await withMemoryManagerForAgent({ @@ -723,7 +733,7 @@ export function registerMemoryCli(program: Command) { run: async (manager) => { let results: Awaited>; try { - results = await manager.search(query, { + results = await manager.search(resolvedQuery, { maxResults: opts.maxResults, minScore: opts.minScore, }); From b5b407462376e047af97a41091ed2bc88124b647 Mon Sep 17 00:00:00 2001 From: SidQin-cyber Date: Wed, 25 Feb 2026 09:35:52 +0800 Subject: [PATCH 2/3] fix(matrix): restore allowlist policy fallback for legacy dm settings Normalize Matrix monitor access policy resolution to honor legacy `dmPolicy`/`allowFrom` fields and reuse DM allowlists for group allowlist mode when group-specific entries are unset. Co-authored-by: Cursor --- .../monitor/index.access-config.test.ts | 41 +++++++++++++++++++ extensions/matrix/src/matrix/monitor/index.ts | 29 +++++++++++-- 2 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 extensions/matrix/src/matrix/monitor/index.access-config.test.ts diff --git a/extensions/matrix/src/matrix/monitor/index.access-config.test.ts b/extensions/matrix/src/matrix/monitor/index.access-config.test.ts new file mode 100644 index 0000000000000..ec2c71c6de418 --- /dev/null +++ b/extensions/matrix/src/matrix/monitor/index.access-config.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from "vitest"; +import type { MatrixConfig } from "../../types.js"; +import { resolveMatrixDmAccessConfig } from "./index.js"; + +describe("resolveMatrixDmAccessConfig", () => { + it("supports legacy dmPolicy + allowFrom fields", () => { + const cfg = { + dmPolicy: "allowlist", + allowFrom: ["@alice:example.org"], + } as unknown as MatrixConfig; + + const resolved = resolveMatrixDmAccessConfig(cfg); + + expect(resolved.dmPolicyRaw).toBe("allowlist"); + expect(resolved.allowFrom).toEqual(["@alice:example.org"]); + expect(resolved.groupAllowFrom).toEqual(["@alice:example.org"]); + }); + + it("falls back group allowlist to dm allowlist when groupAllowFrom is unset", () => { + const cfg = { + dm: { policy: "allowlist", allowFrom: ["@bob:example.org"] }, + } as MatrixConfig; + + const resolved = resolveMatrixDmAccessConfig(cfg); + + expect(resolved.allowFrom).toEqual(["@bob:example.org"]); + expect(resolved.groupAllowFrom).toEqual(["@bob:example.org"]); + }); + + it("keeps explicit groupAllowFrom when provided", () => { + const cfg = { + dm: { policy: "allowlist", allowFrom: ["@bob:example.org"] }, + groupAllowFrom: ["@carol:example.org"], + } as MatrixConfig; + + const resolved = resolveMatrixDmAccessConfig(cfg); + + expect(resolved.allowFrom).toEqual(["@bob:example.org"]); + expect(resolved.groupAllowFrom).toEqual(["@carol:example.org"]); + }); +}); diff --git a/extensions/matrix/src/matrix/monitor/index.ts b/extensions/matrix/src/matrix/monitor/index.ts index 936eabdd34674..dd799313e0417 100644 --- a/extensions/matrix/src/matrix/monitor/index.ts +++ b/extensions/matrix/src/matrix/monitor/index.ts @@ -36,6 +36,28 @@ export type MonitorMatrixOpts = { }; const DEFAULT_MEDIA_MAX_MB = 20; +const VALID_DM_POLICIES = new Set(["pairing", "allowlist", "open", "disabled"]); + +export function resolveMatrixDmAccessConfig(accountConfig: CoreConfig["channels"]["matrix"]) { + const legacyAllowFrom = ( + accountConfig as { + allowFrom?: Array; + } + ).allowFrom; + const allowFromBase = accountConfig.dm?.allowFrom ?? legacyAllowFrom ?? []; + const allowFrom = allowFromBase.map(String); + const groupAllowFrom = + accountConfig.groupAllowFrom && accountConfig.groupAllowFrom.length > 0 + ? accountConfig.groupAllowFrom.map(String) + : allowFrom; + const legacyDmPolicy = (accountConfig as { dmPolicy?: unknown }).dmPolicy; + const dmPolicyCandidate = accountConfig.dm?.policy ?? legacyDmPolicy; + const dmPolicyRaw = + typeof dmPolicyCandidate === "string" && VALID_DM_POLICIES.has(dmPolicyCandidate) + ? dmPolicyCandidate + : "pairing"; + return { allowFrom, groupAllowFrom, dmPolicyRaw }; +} export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promise { if (isBunRuntime()) { @@ -128,8 +150,9 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi const accountConfig = account.config; const allowlistOnly = accountConfig.allowlistOnly === true; - let allowFrom: string[] = (accountConfig.dm?.allowFrom ?? []).map(String); - let groupAllowFrom: string[] = (accountConfig.groupAllowFrom ?? []).map(String); + const accessConfig = resolveMatrixDmAccessConfig(accountConfig); + let allowFrom: string[] = accessConfig.allowFrom; + let groupAllowFrom: string[] = accessConfig.groupAllowFrom; let roomsConfig = accountConfig.groups ?? accountConfig.rooms; allowFrom = await resolveUserAllowlist("matrix dm allowlist", allowFrom); @@ -262,7 +285,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi const threadReplies = accountConfig.threadReplies ?? "inbound"; const dmConfig = accountConfig.dm; const dmEnabled = dmConfig?.enabled ?? true; - const dmPolicyRaw = dmConfig?.policy ?? "pairing"; + const dmPolicyRaw = accessConfig.dmPolicyRaw; const dmPolicy = allowlistOnly && dmPolicyRaw !== "disabled" ? "allowlist" : dmPolicyRaw; const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "matrix"); const mediaMaxMb = opts.mediaMaxMb ?? accountConfig.mediaMaxMb ?? DEFAULT_MEDIA_MAX_MB; From 04e3285ff56370edfb5bd7ffbef2c294fe85de05 Mon Sep 17 00:00:00 2001 From: SidQin-cyber Date: Wed, 25 Feb 2026 09:36:04 +0800 Subject: [PATCH 3/3] Revert "fix(matrix): restore allowlist policy fallback for legacy dm settings" This reverts commit b5b407462376e047af97a41091ed2bc88124b647. --- .../monitor/index.access-config.test.ts | 41 ------------------- extensions/matrix/src/matrix/monitor/index.ts | 29 ++----------- 2 files changed, 3 insertions(+), 67 deletions(-) delete mode 100644 extensions/matrix/src/matrix/monitor/index.access-config.test.ts diff --git a/extensions/matrix/src/matrix/monitor/index.access-config.test.ts b/extensions/matrix/src/matrix/monitor/index.access-config.test.ts deleted file mode 100644 index ec2c71c6de418..0000000000000 --- a/extensions/matrix/src/matrix/monitor/index.access-config.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { describe, expect, it } from "vitest"; -import type { MatrixConfig } from "../../types.js"; -import { resolveMatrixDmAccessConfig } from "./index.js"; - -describe("resolveMatrixDmAccessConfig", () => { - it("supports legacy dmPolicy + allowFrom fields", () => { - const cfg = { - dmPolicy: "allowlist", - allowFrom: ["@alice:example.org"], - } as unknown as MatrixConfig; - - const resolved = resolveMatrixDmAccessConfig(cfg); - - expect(resolved.dmPolicyRaw).toBe("allowlist"); - expect(resolved.allowFrom).toEqual(["@alice:example.org"]); - expect(resolved.groupAllowFrom).toEqual(["@alice:example.org"]); - }); - - it("falls back group allowlist to dm allowlist when groupAllowFrom is unset", () => { - const cfg = { - dm: { policy: "allowlist", allowFrom: ["@bob:example.org"] }, - } as MatrixConfig; - - const resolved = resolveMatrixDmAccessConfig(cfg); - - expect(resolved.allowFrom).toEqual(["@bob:example.org"]); - expect(resolved.groupAllowFrom).toEqual(["@bob:example.org"]); - }); - - it("keeps explicit groupAllowFrom when provided", () => { - const cfg = { - dm: { policy: "allowlist", allowFrom: ["@bob:example.org"] }, - groupAllowFrom: ["@carol:example.org"], - } as MatrixConfig; - - const resolved = resolveMatrixDmAccessConfig(cfg); - - expect(resolved.allowFrom).toEqual(["@bob:example.org"]); - expect(resolved.groupAllowFrom).toEqual(["@carol:example.org"]); - }); -}); diff --git a/extensions/matrix/src/matrix/monitor/index.ts b/extensions/matrix/src/matrix/monitor/index.ts index dd799313e0417..936eabdd34674 100644 --- a/extensions/matrix/src/matrix/monitor/index.ts +++ b/extensions/matrix/src/matrix/monitor/index.ts @@ -36,28 +36,6 @@ export type MonitorMatrixOpts = { }; const DEFAULT_MEDIA_MAX_MB = 20; -const VALID_DM_POLICIES = new Set(["pairing", "allowlist", "open", "disabled"]); - -export function resolveMatrixDmAccessConfig(accountConfig: CoreConfig["channels"]["matrix"]) { - const legacyAllowFrom = ( - accountConfig as { - allowFrom?: Array; - } - ).allowFrom; - const allowFromBase = accountConfig.dm?.allowFrom ?? legacyAllowFrom ?? []; - const allowFrom = allowFromBase.map(String); - const groupAllowFrom = - accountConfig.groupAllowFrom && accountConfig.groupAllowFrom.length > 0 - ? accountConfig.groupAllowFrom.map(String) - : allowFrom; - const legacyDmPolicy = (accountConfig as { dmPolicy?: unknown }).dmPolicy; - const dmPolicyCandidate = accountConfig.dm?.policy ?? legacyDmPolicy; - const dmPolicyRaw = - typeof dmPolicyCandidate === "string" && VALID_DM_POLICIES.has(dmPolicyCandidate) - ? dmPolicyCandidate - : "pairing"; - return { allowFrom, groupAllowFrom, dmPolicyRaw }; -} export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promise { if (isBunRuntime()) { @@ -150,9 +128,8 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi const accountConfig = account.config; const allowlistOnly = accountConfig.allowlistOnly === true; - const accessConfig = resolveMatrixDmAccessConfig(accountConfig); - let allowFrom: string[] = accessConfig.allowFrom; - let groupAllowFrom: string[] = accessConfig.groupAllowFrom; + let allowFrom: string[] = (accountConfig.dm?.allowFrom ?? []).map(String); + let groupAllowFrom: string[] = (accountConfig.groupAllowFrom ?? []).map(String); let roomsConfig = accountConfig.groups ?? accountConfig.rooms; allowFrom = await resolveUserAllowlist("matrix dm allowlist", allowFrom); @@ -285,7 +262,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi const threadReplies = accountConfig.threadReplies ?? "inbound"; const dmConfig = accountConfig.dm; const dmEnabled = dmConfig?.enabled ?? true; - const dmPolicyRaw = accessConfig.dmPolicyRaw; + const dmPolicyRaw = dmConfig?.policy ?? "pairing"; const dmPolicy = allowlistOnly && dmPolicyRaw !== "disabled" ? "allowlist" : dmPolicyRaw; const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "matrix"); const mediaMaxMb = opts.mediaMaxMb ?? accountConfig.mediaMaxMb ?? DEFAULT_MEDIA_MAX_MB;