From 2db042011c72204da1c9ade464142592a57bc447 Mon Sep 17 00:00:00 2001 From: VoidFreud Date: Tue, 10 Mar 2026 14:38:44 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20fix=20OpenClaw=20plugin?= =?UTF-8?q?=20API=20compatibility=20with=20EverMemOS=20v1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plugin was not working out of the box with the current EverMemOS API. Several request format mismatches caused 404, 405, 422, and fetch errors when used with OpenClaw. Fixes: 1. API version: /api/v0/ → /api/v1/ The plugin hardcoded /api/v0/ for both search and capture endpoints, but EverMemOS serves its API under /api/v1/. All requests returned 404. 2. Search field name: group_ids (array) → group_id (string) The plugin sent `group_ids: ["group-name"]` but the EverMemOS /memories/search endpoint expects `group_id: "group-name"` (a plain string). The array field was silently ignored, causing unscoped searches. 3. Capture role mapping: "tool" → "assistant" OpenClaw agent conversations include messages with role "tool" (tool call results). EverMemOS validates roles strictly and only accepts "user" or "assistant", returning HTTP 422 for any other value. Tool and system messages are now mapped to "assistant" since they are part of the assistant's workflow. 4. Search memory_types: array → comma-separated string The plugin sent memory_types as repeated query parameters (memory_types=a&memory_types=b). However, EverMemOS collects query params via dict(request.query_params) which flattens multi-value keys, keeping only the last value. The _parse_memory_types() function already supports comma-separated strings, so sending "a,b" works correctly. 5. README: fix example load.paths Changed the example path from "/path/to/EverMemOS-OpenClaw-Plugin" (wrong case, not a real path) to "~/.openclaw/extensions/evermemos- openclaw-plugin" which is the standard OpenClaw extensions directory. Note: OpenClaw resolves relative paths from the gateway process's working directory (process.cwd()), not from ~/.openclaw/, so using ~/ or absolute paths is required. Co-Authored-By: Claude Opus 4.6 --- evermemos-openclaw-plugin/README.md | 2 +- evermemos-openclaw-plugin/index.js | 2 +- evermemos-openclaw-plugin/src/memory-api.js | 15 ++++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/evermemos-openclaw-plugin/README.md b/evermemos-openclaw-plugin/README.md index 364b9751..ec38319a 100644 --- a/evermemos-openclaw-plugin/README.md +++ b/evermemos-openclaw-plugin/README.md @@ -25,7 +25,7 @@ Add the plugin directory to `plugins.load.paths` and enable it under `plugins.en "memory": "evermemos-openclaw-plugin" }, "load": { - "paths": ["/path/to/EverMemOS-OpenClaw-Plugin"] + "paths": ["~/.openclaw/extensions/evermemos-openclaw-plugin"] }, "entries": { "evermemos-openclaw-plugin": { diff --git a/evermemos-openclaw-plugin/index.js b/evermemos-openclaw-plugin/index.js index 9ac5d141..69e342f2 100644 --- a/evermemos-openclaw-plugin/index.js +++ b/evermemos-openclaw-plugin/index.js @@ -28,7 +28,7 @@ export default { const params = { query, user_id: cfg.userId, - group_ids: cfg.groupId ? [cfg.groupId] : undefined, + group_id: cfg.groupId || undefined, memory_types: cfg.memoryTypes, retrieve_method: cfg.retrieveMethod, top_k: cfg.topK, diff --git a/evermemos-openclaw-plugin/src/memory-api.js b/evermemos-openclaw-plugin/src/memory-api.js index d90f0c9b..ff7eff57 100644 --- a/evermemos-openclaw-plugin/src/memory-api.js +++ b/evermemos-openclaw-plugin/src/memory-api.js @@ -12,9 +12,9 @@ export async function searchMemories(cfg, params) { const results = await Promise.all( searches.map(async ({ label, types }) => { - const p = { ...baseParams, memory_types: types }; - console.log("[memory-api] GET /api/v0/memories/search", label, JSON.stringify(p)); - const r = await request(cfg, "GET", "/api/v0/memories/search", p); + const p = { ...baseParams, memory_types: types.join(",") }; + console.log("[memory-api] GET /api/v1/memories/search", label, JSON.stringify(p)); + const r = await request(cfg, "GET", "/api/v1/memories/search", p); console.log("[memory-api] GET response", label, JSON.stringify(r)); return r; }), @@ -39,8 +39,9 @@ export async function saveMemories(cfg, { userId, groupId, messages = [], flush if (!messages.length) return; const stamp = Date.now(); for (let i = 0; i < messages.length; i++) { - const { role = "user", content = "", tool_calls, tool_call_id } = messages[i]; - const sender = role === "assistant" ? role : (role === "tool" ? "tool" : userId); + const { role: rawRole = "user", content = "", tool_calls, tool_call_id } = messages[i]; + const role = rawRole === "user" ? "user" : "assistant"; + const sender = role === "assistant" ? role : userId; const isLast = i === messages.length - 1; const payload = { @@ -58,8 +59,8 @@ export async function saveMemories(cfg, { userId, groupId, messages = [], flush ...(tool_call_id && { tool_call_id }), ...(flush && isLast && { flush: true }), }; - console.log("[memory-api] POST /api/v0/memories", JSON.stringify(payload)); - const result = await request(cfg, "POST", "/api/v0/memories", payload); + console.log("[memory-api] POST /api/v1/memories", JSON.stringify(payload)); + const result = await request(cfg, "POST", "/api/v1/memories", payload); console.log("[memory-api] POST response", JSON.stringify(result)); } }