fix: update command on docs and logic (#4946)
This commit is contained in:
@@ -4,6 +4,30 @@ description: "Release notes for the OpenClaw plugin and agent harness."
|
|||||||
mode: "wide"
|
mode: "wide"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Update label="2026-04-23" description="v1.0.10">
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
</Update>
|
||||||
|
|
||||||
<Update label="2026-04-22" description="v1.0.9">
|
<Update label="2026-04-22" description="v1.0.9">
|
||||||
|
|
||||||
**Security & Compliance:**
|
**Security & Compliance:**
|
||||||
|
|||||||
@@ -362,11 +362,9 @@ Everything inside `oss` is optional — defaults use OpenAI embeddings (`text-em
|
|||||||
### Updating the Plugin
|
### Updating the Plugin
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openclaw plugins update @mem0/openclaw-mem0
|
openclaw plugins update openclaw-mem0
|
||||||
```
|
```
|
||||||
|
|
||||||
<Note>Use the npm package name (`@mem0/openclaw-mem0`) for plugin management commands, not the plugin ID (`openclaw-mem0`).</Note>
|
|
||||||
|
|
||||||
### Checking Plugin Status
|
### Checking Plugin Status
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -411,8 +409,9 @@ If the plugin installs but doesn't work:
|
|||||||
|
|
||||||
If `openclaw plugins update` fails:
|
If `openclaw plugins update` fails:
|
||||||
|
|
||||||
1. Use the full npm package name: `openclaw plugins update @mem0/openclaw-mem0`
|
1. Use the plugin ID: `openclaw plugins update openclaw-mem0`
|
||||||
2. If that fails, uninstall and reinstall:
|
2. Update all plugins at once: `openclaw plugins update --all`
|
||||||
|
3. If that fails, uninstall and reinstall:
|
||||||
```bash
|
```bash
|
||||||
openclaw plugins uninstall openclaw-mem0
|
openclaw plugins uninstall openclaw-mem0
|
||||||
openclaw plugins install @mem0/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.
|
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.
|
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
<CardGroup cols={2}>
|
||||||
<Card title="OpenAI Agents SDK" icon="robot" href="/integrations/openai-agents-sdk">
|
<Card title="OpenAI Agents SDK" icon="robot" href="/integrations/openai-agents-sdk">
|
||||||
Build agents with OpenAI's SDK and Mem0
|
Build agents with OpenAI's SDK and Mem0
|
||||||
|
|||||||
+7
-1
@@ -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.
|
> **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)
|
### Open-Source (Self-hosted)
|
||||||
|
|
||||||
No Mem0 key needed. Vectors are stored locally in SQLite at `~/.mem0/vector_store.db` — no external database required.
|
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 |
|
| `autoRecall` | `boolean` | `false` | Inject relevant memories before each turn |
|
||||||
| `autoCapture` | `boolean` | `false` | Extract and store facts after each turn |
|
| `autoCapture` | `boolean` | `false` | Extract and store facts after each turn |
|
||||||
| `topK` | `number` | `5` | Max memories returned per recall |
|
| `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
|
### Platform Mode
|
||||||
|
|
||||||
|
|||||||
@@ -141,10 +141,12 @@ export function ensureInstallRecord(): void {
|
|||||||
const entry = full?.plugins?.entries?.[PLUGIN_ID];
|
const entry = full?.plugins?.entries?.[PLUGIN_ID];
|
||||||
const record = full?.plugins?.installs?.[PLUGIN_ID];
|
const record = full?.plugins?.installs?.[PLUGIN_ID];
|
||||||
const allow = full?.plugins?.allow;
|
const allow = full?.plugins?.allow;
|
||||||
|
const specPinned = record?.spec && /\d+\.\d+\.\d+/.test(record.spec);
|
||||||
if (
|
if (
|
||||||
entry?.enabled === true &&
|
entry?.enabled === true &&
|
||||||
record?.source &&
|
record?.source &&
|
||||||
record?.spec &&
|
record?.spec &&
|
||||||
|
!specPinned &&
|
||||||
Array.isArray(allow) &&
|
Array.isArray(allow) &&
|
||||||
allow.includes(PLUGIN_ID)
|
allow.includes(PLUGIN_ID)
|
||||||
) {
|
) {
|
||||||
@@ -171,8 +173,10 @@ export function ensureInstallRecord(): void {
|
|||||||
record.source = "npm";
|
record.source = "npm";
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
if (!record.spec) {
|
if (!record.spec || /\d+\.\d+\.\d+/.test(record.spec)) {
|
||||||
record.spec = `${NPM_PACKAGE}@latest`;
|
record.spec = record.source === "clawhub"
|
||||||
|
? `clawhub:${NPM_PACKAGE}`
|
||||||
|
: `${NPM_PACKAGE}@latest`;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
if (!record.resolvedName) {
|
if (!record.resolvedName) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"id": "openclaw-mem0",
|
"id": "openclaw-mem0",
|
||||||
"name": "Memory (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.",
|
"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",
|
"kind": "memory",
|
||||||
"skills": ["skills"],
|
"skills": ["skills"],
|
||||||
"commandAliases": [
|
"commandAliases": [
|
||||||
@@ -103,8 +103,8 @@
|
|||||||
},
|
},
|
||||||
"searchThreshold": {
|
"searchThreshold": {
|
||||||
"label": "Search Threshold",
|
"label": "Search Threshold",
|
||||||
"placeholder": "0.5",
|
"placeholder": "0.3",
|
||||||
"help": "Minimum similarity score for search results (0-1). Default: 0.5"
|
"help": "Minimum similarity score for search results (0-1). Default: 0.3"
|
||||||
},
|
},
|
||||||
"topK": {
|
"topK": {
|
||||||
"label": "Top K Results",
|
"label": "Top K Results",
|
||||||
@@ -295,34 +295,18 @@
|
|||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
},
|
},
|
||||||
"setup": {
|
"providerEndpoints": [
|
||||||
"providers": [
|
{
|
||||||
{
|
"endpointClass": "api",
|
||||||
"id": "mem0",
|
"hosts": ["api.mem0.ai"]
|
||||||
"authMethods": ["api-key"],
|
},
|
||||||
"envVars": ["MEM0_API_KEY"],
|
{
|
||||||
"description": "Platform mode: hosted memory at mem0.ai"
|
"endpointClass": "dashboard",
|
||||||
},
|
"hosts": ["app.mem0.ai"]
|
||||||
{
|
},
|
||||||
"id": "openclaw-mem0-oss",
|
{
|
||||||
"authMethods": ["api-key", "config"],
|
"endpointClass": "telemetry",
|
||||||
"envVars": ["OPENAI_API_KEY", "ANTHROPIC_API_KEY"],
|
"hosts": ["us.i.posthog.com"]
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mem0/openclaw-mem0",
|
"name": "@mem0/openclaw-mem0",
|
||||||
"version": "1.0.9",
|
"version": "1.0.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Mem0 memory backend for OpenClaw — platform or self-hosted open-source",
|
"description": "Mem0 memory backend for OpenClaw — platform or self-hosted open-source",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
|||||||
+12
-18
@@ -11,7 +11,8 @@
|
|||||||
import { createHash, randomUUID } from "node:crypto";
|
import { createHash, randomUUID } from "node:crypto";
|
||||||
import { readPluginAuth, writePluginAuth, getBaseUrl, clearAnonymousTelemetryId } from "./cli/config-file.ts";
|
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_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX";
|
||||||
const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/";
|
const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/";
|
||||||
@@ -129,18 +130,11 @@ function maybeResolveEmail(apiKey: string): void {
|
|||||||
} catch {
|
} catch {
|
||||||
/* ignore */
|
/* ignore */
|
||||||
}
|
}
|
||||||
// Upgrade any already-queued events from md5(apiKey) to email
|
const oldId = createHash("sha256").update(apiKey).digest("hex");
|
||||||
const oldId = createHash("md5").update(apiKey).digest("hex");
|
const newId = createHash("sha256").update(email).digest("hex");
|
||||||
for (const ev of eventQueue) {
|
for (const ev of eventQueue) {
|
||||||
if (ev.distinct_id === oldId) {
|
if (ev.distinct_id === oldId) {
|
||||||
ev.distinct_id = email;
|
ev.distinct_id = newId;
|
||||||
}
|
|
||||||
// Also upgrade $identify's distinct_id if present
|
|
||||||
if (
|
|
||||||
ev.event === "$identify" &&
|
|
||||||
ev.distinct_id === oldId
|
|
||||||
) {
|
|
||||||
ev.distinct_id = email;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,12 +170,14 @@ function isTelemetryEnabled(): boolean {
|
|||||||
function getDistinctId(apiKey?: string): string {
|
function getDistinctId(apiKey?: string): string {
|
||||||
try {
|
try {
|
||||||
const auth = readPluginAuth();
|
const auth = readPluginAuth();
|
||||||
if (auth.userEmail) return auth.userEmail;
|
if (auth.userEmail) {
|
||||||
|
return createHash("sha256").update(auth.userEmail).digest("hex");
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* ignore */
|
/* ignore */
|
||||||
}
|
}
|
||||||
if (apiKey) {
|
if (apiKey) {
|
||||||
return createHash("md5").update(apiKey).digest("hex");
|
return createHash("sha256").update(apiKey).digest("hex");
|
||||||
}
|
}
|
||||||
return getOrCreateAnonymousId();
|
return getOrCreateAnonymousId();
|
||||||
}
|
}
|
||||||
@@ -262,11 +258,9 @@ export function captureEvent(
|
|||||||
try {
|
try {
|
||||||
const distinctId = getDistinctId(ctx?.apiKey);
|
const distinctId = getDistinctId(ctx?.apiKey);
|
||||||
|
|
||||||
// If we resolved to md5(apiKey) instead of email, kick off a background
|
let hasEmail = false;
|
||||||
// /v1/ping/ to resolve and cache the email. The current event ships with
|
try { hasEmail = !!readPluginAuth().userEmail; } catch { /* ignore */ }
|
||||||
// the hash, but the async resolution upgrades any still-queued events
|
if (ctx?.apiKey && !hasEmail && !distinctId.startsWith("openclaw-anon-")) {
|
||||||
// (including this one) before the beforeExit flush fires.
|
|
||||||
if (ctx?.apiKey && distinctId && !distinctId.includes("@") && !distinctId.startsWith("openclaw-anon-")) {
|
|
||||||
maybeResolveEmail(ctx.apiKey);
|
maybeResolveEmail(ctx.apiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ vi.mock("../cli/config-file.ts", () => ({
|
|||||||
readPluginAuth: vi.fn().mockReturnValue({}),
|
readPluginAuth: vi.fn().mockReturnValue({}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { captureEvent, PLUGIN_VERSION } from "../telemetry.ts";
|
import { captureEvent } from "../telemetry.ts";
|
||||||
import { readPluginAuth } from "../cli/config-file.ts";
|
import { readPluginAuth } from "../cli/config-file.ts";
|
||||||
|
|
||||||
describe("telemetry", () => {
|
describe("telemetry", () => {
|
||||||
@@ -23,10 +23,6 @@ describe("telemetry", () => {
|
|||||||
delete (globalThis as any).__mem0_telemetry_override;
|
delete (globalThis as any).__mem0_telemetry_override;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("exports PLUGIN_VERSION", () => {
|
|
||||||
expect(PLUGIN_VERSION).toBe("1.0.7");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("captureEvent does not throw", () => {
|
it("captureEvent does not throw", () => {
|
||||||
expect(() => captureEvent("test_event")).not.toThrow();
|
expect(() => captureEvent("test_event")).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defineConfig } from "tsup";
|
import { defineConfig } from "tsup";
|
||||||
|
import pkg from "./package.json";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
entry: ["index.ts", "fs-safe.ts"],
|
entry: ["index.ts", "fs-safe.ts"],
|
||||||
@@ -8,4 +9,7 @@ export default defineConfig({
|
|||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
clean: true,
|
clean: true,
|
||||||
external: [/^node:/, /^openclaw\//, "fs", "os", "path", "url", "readline", "module"],
|
external: [/^node:/, /^openclaw\//, "fs", "os", "path", "url", "readline", "module"],
|
||||||
|
define: {
|
||||||
|
__OPENCLAW_PLUGIN_VERSION__: JSON.stringify(pkg.version),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { defineConfig } from "vitest/config";
|
import { defineConfig } from "vitest/config";
|
||||||
|
import pkg from "./package.json";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
define: {
|
||||||
|
__OPENCLAW_PLUGIN_VERSION__: JSON.stringify(pkg.version),
|
||||||
|
},
|
||||||
test: {
|
test: {
|
||||||
alias: {
|
alias: {
|
||||||
// OpenClaw SDK modules are resolved from the gateway at runtime.
|
// OpenClaw SDK modules are resolved from the gateway at runtime.
|
||||||
|
|||||||
Reference in New Issue
Block a user