diff --git a/docs/changelog/openclaw.mdx b/docs/changelog/openclaw.mdx
index b10ac249b..c5ff0f58b 100644
--- a/docs/changelog/openclaw.mdx
+++ b/docs/changelog/openclaw.mdx
@@ -4,6 +4,30 @@ description: "Release notes for the OpenClaw plugin and agent harness."
mode: "wide"
---
+
+
+**Security:**
+- Telemetry `distinct_id` now uses SHA-256 instead of MD5 — prevents rainbow-table reversal of API key hashes
+- User email is now SHA-256 hashed before sending as `distinct_id` — no PII in telemetry payloads
+- Declared PostHog telemetry endpoint (`us.i.posthog.com`) in `providerEndpoints`
+
+**Fixes:**
+- Fixed version-pinned install records preventing plugin updates. `ensureInstallRecord()` now detects semver-pinned specs (e.g. `@mem0/openclaw-mem0@1.0.7`) and rewrites them to `@latest` or `clawhub:` prefix so `openclaw plugins update` resolves to the newest release
+- Fixed `searchThreshold` default inconsistency: standardized to `0.3` across docs, README, and manifest
+- `PLUGIN_VERSION` now injected at build time via tsup `define` from `package.json` — no more hardcoded version strings
+
+**Manifest Compliance:**
+- Removed non-spec fields: `requiredEnvVars`, `dataLocations`, `privacy`, `setup` (with `externalEndpoints`, `providers`, `requiresRuntime`, `postInstallHint`)
+- Replaced `setup.externalEndpoints` with spec-compliant `providerEndpoints` using `endpointClass` + `hosts` format
+- Env var declarations now rely solely on `providerAuthEnvVars` (already spec-compliant)
+
+**Docs:**
+- Fixed `openclaw plugins update` command: uses plugin ID (`openclaw-mem0`), not npm package name (`@mem0/openclaw-mem0`)
+- Added update section to README
+- Removed redundant "Key Features" and "Conclusion" sections from integration docs
+
+
+
**Security & Compliance:**
diff --git a/docs/integrations/openclaw.mdx b/docs/integrations/openclaw.mdx
index 06157e253..56e39fb70 100644
--- a/docs/integrations/openclaw.mdx
+++ b/docs/integrations/openclaw.mdx
@@ -362,11 +362,9 @@ Everything inside `oss` is optional — defaults use OpenAI embeddings (`text-em
### Updating the Plugin
```bash
-openclaw plugins update @mem0/openclaw-mem0
+openclaw plugins update openclaw-mem0
```
-Use the npm package name (`@mem0/openclaw-mem0`) for plugin management commands, not the plugin ID (`openclaw-mem0`).
-
### Checking Plugin Status
```bash
@@ -411,8 +409,9 @@ If the plugin installs but doesn't work:
If `openclaw plugins update` fails:
-1. Use the full npm package name: `openclaw plugins update @mem0/openclaw-mem0`
-2. If that fails, uninstall and reinstall:
+1. Use the plugin ID: `openclaw plugins update openclaw-mem0`
+2. Update all plugins at once: `openclaw plugins update --all`
+3. If that fails, uninstall and reinstall:
```bash
openclaw plugins uninstall openclaw-mem0
openclaw plugins install @mem0/openclaw-mem0
@@ -476,17 +475,6 @@ export MEM0_TELEMETRY=false
The plugin injects memory-related instructions into the agent's system context via OpenClaw's `prependSystemContext` mechanism. This includes the memory triage protocol and recalled memories. This is the standard OpenClaw plugin SDK pattern for memory backends — no user-facing prompts are modified.
-## Key Features
-
-1. **Zero Configuration** — Auto-recall and auto-capture work out of the box with no prompting required
-2. **Dual Memory Scopes** — Session-scoped short-term and user-scoped long-term memories
-3. **Flexible Backend** — Use Mem0 Cloud for managed service or self-host with open-source mode
-4. **Rich Tool Suite** — Eight agent tools for explicit memory operations when needed
-
-## Conclusion
-
-The `@mem0/openclaw-mem0` plugin gives OpenClaw agents persistent memory with minimal setup. Whether using Mem0 Cloud or self-hosting, your agents can now remember user preferences, facts, and context across sessions automatically.
-
Build agents with OpenAI's SDK and Mem0
diff --git a/openclaw/README.md b/openclaw/README.md
index 7e63b318a..c18f50e8d 100644
--- a/openclaw/README.md
+++ b/openclaw/README.md
@@ -60,6 +60,12 @@ openclaw --version
> **Note:** OpenClaw memory plugins load through an exclusive slot, so install alone does not activate the plugin. You must set `plugins.slots.memory` as shown above.
+### Updating the plugin to get the latest features and fixes:
+
+```bash
+openclaw plugins update openclaw-mem0
+```
+
### Open-Source (Self-hosted)
No Mem0 key needed. Vectors are stored locally in SQLite at `~/.mem0/vector_store.db` — no external database required.
@@ -257,7 +263,7 @@ openclaw mem0 help --json # discover all comma
| `autoRecall` | `boolean` | `false` | Inject relevant memories before each turn |
| `autoCapture` | `boolean` | `false` | Extract and store facts after each turn |
| `topK` | `number` | `5` | Max memories returned per recall |
-| `searchThreshold` | `number` | `0.5` | Minimum similarity score (0-1) |
+| `searchThreshold` | `number` | `0.3` | Minimum similarity score (0-1) |
### Platform Mode
diff --git a/openclaw/cli/config-file.ts b/openclaw/cli/config-file.ts
index 3f9aa2ffe..bba4a8dc3 100644
--- a/openclaw/cli/config-file.ts
+++ b/openclaw/cli/config-file.ts
@@ -141,10 +141,12 @@ export function ensureInstallRecord(): void {
const entry = full?.plugins?.entries?.[PLUGIN_ID];
const record = full?.plugins?.installs?.[PLUGIN_ID];
const allow = full?.plugins?.allow;
+ const specPinned = record?.spec && /\d+\.\d+\.\d+/.test(record.spec);
if (
entry?.enabled === true &&
record?.source &&
record?.spec &&
+ !specPinned &&
Array.isArray(allow) &&
allow.includes(PLUGIN_ID)
) {
@@ -171,8 +173,10 @@ export function ensureInstallRecord(): void {
record.source = "npm";
changed = true;
}
- if (!record.spec) {
- record.spec = `${NPM_PACKAGE}@latest`;
+ if (!record.spec || /\d+\.\d+\.\d+/.test(record.spec)) {
+ record.spec = record.source === "clawhub"
+ ? `clawhub:${NPM_PACKAGE}`
+ : `${NPM_PACKAGE}@latest`;
changed = true;
}
if (!record.resolvedName) {
diff --git a/openclaw/openclaw.plugin.json b/openclaw/openclaw.plugin.json
index e30e0e3ec..d0457fee3 100644
--- a/openclaw/openclaw.plugin.json
+++ b/openclaw/openclaw.plugin.json
@@ -2,7 +2,7 @@
"id": "openclaw-mem0",
"name": "Memory (Mem0)",
"description": "Mem0 memory backend for OpenClaw — platform (mem0.ai cloud) or self-hosted open-source. Auto-recall and auto-capture are opt-in (disabled by default). Supports OpenAI, Anthropic, Ollama (fully local), Qdrant, and PGVector providers.",
- "version": "1.0.9",
+ "version": "1.0.10",
"kind": "memory",
"skills": ["skills"],
"commandAliases": [
@@ -103,8 +103,8 @@
},
"searchThreshold": {
"label": "Search Threshold",
- "placeholder": "0.5",
- "help": "Minimum similarity score for search results (0-1). Default: 0.5"
+ "placeholder": "0.3",
+ "help": "Minimum similarity score for search results (0-1). Default: 0.3"
},
"topK": {
"label": "Top K Results",
@@ -295,34 +295,18 @@
},
"required": []
},
- "setup": {
- "providers": [
- {
- "id": "mem0",
- "authMethods": ["api-key"],
- "envVars": ["MEM0_API_KEY"],
- "description": "Platform mode: hosted memory at mem0.ai"
- },
- {
- "id": "openclaw-mem0-oss",
- "authMethods": ["api-key", "config"],
- "envVars": ["OPENAI_API_KEY", "ANTHROPIC_API_KEY"],
- "description": "Open-source mode: self-hosted with chosen LLM/embedder providers. No env vars needed when using Ollama (local)."
- }
- ],
- "requiresRuntime": false,
- "postInstallHint": "Run 'openclaw mem0 init' to configure mode and credentials",
- "externalEndpoints": [
- {
- "url": "https://api.mem0.ai",
- "purpose": "Platform mode API — memory storage and retrieval",
- "required": "platform mode only"
- },
- {
- "url": "https://app.mem0.ai",
- "purpose": "Account dashboard and API key management",
- "required": "platform mode only"
- }
- ]
- }
+ "providerEndpoints": [
+ {
+ "endpointClass": "api",
+ "hosts": ["api.mem0.ai"]
+ },
+ {
+ "endpointClass": "dashboard",
+ "hosts": ["app.mem0.ai"]
+ },
+ {
+ "endpointClass": "telemetry",
+ "hosts": ["us.i.posthog.com"]
+ }
+ ]
}
\ No newline at end of file
diff --git a/openclaw/package.json b/openclaw/package.json
index 65084b4ea..1ce60418b 100644
--- a/openclaw/package.json
+++ b/openclaw/package.json
@@ -1,6 +1,6 @@
{
"name": "@mem0/openclaw-mem0",
- "version": "1.0.9",
+ "version": "1.0.10",
"type": "module",
"description": "Mem0 memory backend for OpenClaw — platform or self-hosted open-source",
"license": "Apache-2.0",
diff --git a/openclaw/telemetry.ts b/openclaw/telemetry.ts
index 93497f2a5..3e2b4c7a4 100644
--- a/openclaw/telemetry.ts
+++ b/openclaw/telemetry.ts
@@ -11,7 +11,8 @@
import { createHash, randomUUID } from "node:crypto";
import { readPluginAuth, writePluginAuth, getBaseUrl, clearAnonymousTelemetryId } from "./cli/config-file.ts";
-export const PLUGIN_VERSION = "1.0.7";
+declare const __OPENCLAW_PLUGIN_VERSION__: string;
+export const PLUGIN_VERSION: string = __OPENCLAW_PLUGIN_VERSION__;
const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX";
const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/";
@@ -129,18 +130,11 @@ function maybeResolveEmail(apiKey: string): void {
} catch {
/* ignore */
}
- // Upgrade any already-queued events from md5(apiKey) to email
- const oldId = createHash("md5").update(apiKey).digest("hex");
+ const oldId = createHash("sha256").update(apiKey).digest("hex");
+ const newId = createHash("sha256").update(email).digest("hex");
for (const ev of eventQueue) {
if (ev.distinct_id === oldId) {
- ev.distinct_id = email;
- }
- // Also upgrade $identify's distinct_id if present
- if (
- ev.event === "$identify" &&
- ev.distinct_id === oldId
- ) {
- ev.distinct_id = email;
+ ev.distinct_id = newId;
}
}
}
@@ -176,12 +170,14 @@ function isTelemetryEnabled(): boolean {
function getDistinctId(apiKey?: string): string {
try {
const auth = readPluginAuth();
- if (auth.userEmail) return auth.userEmail;
+ if (auth.userEmail) {
+ return createHash("sha256").update(auth.userEmail).digest("hex");
+ }
} catch {
/* ignore */
}
if (apiKey) {
- return createHash("md5").update(apiKey).digest("hex");
+ return createHash("sha256").update(apiKey).digest("hex");
}
return getOrCreateAnonymousId();
}
@@ -262,11 +258,9 @@ export function captureEvent(
try {
const distinctId = getDistinctId(ctx?.apiKey);
- // If we resolved to md5(apiKey) instead of email, kick off a background
- // /v1/ping/ to resolve and cache the email. The current event ships with
- // the hash, but the async resolution upgrades any still-queued events
- // (including this one) before the beforeExit flush fires.
- if (ctx?.apiKey && distinctId && !distinctId.includes("@") && !distinctId.startsWith("openclaw-anon-")) {
+ let hasEmail = false;
+ try { hasEmail = !!readPluginAuth().userEmail; } catch { /* ignore */ }
+ if (ctx?.apiKey && !hasEmail && !distinctId.startsWith("openclaw-anon-")) {
maybeResolveEmail(ctx.apiKey);
}
diff --git a/openclaw/tests/telemetry.test.ts b/openclaw/tests/telemetry.test.ts
index 20abcaaa2..9a7bee716 100644
--- a/openclaw/tests/telemetry.test.ts
+++ b/openclaw/tests/telemetry.test.ts
@@ -5,7 +5,7 @@ vi.mock("../cli/config-file.ts", () => ({
readPluginAuth: vi.fn().mockReturnValue({}),
}));
-import { captureEvent, PLUGIN_VERSION } from "../telemetry.ts";
+import { captureEvent } from "../telemetry.ts";
import { readPluginAuth } from "../cli/config-file.ts";
describe("telemetry", () => {
@@ -23,10 +23,6 @@ describe("telemetry", () => {
delete (globalThis as any).__mem0_telemetry_override;
});
- it("exports PLUGIN_VERSION", () => {
- expect(PLUGIN_VERSION).toBe("1.0.7");
- });
-
it("captureEvent does not throw", () => {
expect(() => captureEvent("test_event")).not.toThrow();
});
diff --git a/openclaw/tsup.config.ts b/openclaw/tsup.config.ts
index 397df11fe..95b5bdfa0 100644
--- a/openclaw/tsup.config.ts
+++ b/openclaw/tsup.config.ts
@@ -1,4 +1,5 @@
import { defineConfig } from "tsup";
+import pkg from "./package.json";
export default defineConfig({
entry: ["index.ts", "fs-safe.ts"],
@@ -8,4 +9,7 @@ export default defineConfig({
sourcemap: true,
clean: true,
external: [/^node:/, /^openclaw\//, "fs", "os", "path", "url", "readline", "module"],
+ define: {
+ __OPENCLAW_PLUGIN_VERSION__: JSON.stringify(pkg.version),
+ },
});
diff --git a/openclaw/vitest.config.ts b/openclaw/vitest.config.ts
index 94b1bab79..d9eb26694 100644
--- a/openclaw/vitest.config.ts
+++ b/openclaw/vitest.config.ts
@@ -1,6 +1,10 @@
import { defineConfig } from "vitest/config";
+import pkg from "./package.json";
export default defineConfig({
+ define: {
+ __OPENCLAW_PLUGIN_VERSION__: JSON.stringify(pkg.version),
+ },
test: {
alias: {
// OpenClaw SDK modules are resolved from the gateway at runtime.