@mem9/mem9 0.4.0-rc.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # OpenClaw Plugin for mem9
1
+ # OpenClaw Plugin for mnemos
2
2
 
3
- Memory plugin for [OpenClaw](https://github.com/openclaw) — replaces the built-in memory slot with cloud-persistent shared memory. Runs in server mode only, connecting to `mnemo-server` via `apiUrl` + `apiKey` (preferred) or legacy `tenantID`. Optional `provisionQueryParams` can forward `utm_*` attribution only during first-time auto-provisioning.
3
+ Memory plugin for [OpenClaw](https://github.com/openclaw) — replaces the built-in memory slot with cloud-persistent shared memory. Runs in server mode only, connecting to `mnemo-server` via `apiUrl` + `apiKey` (preferred) or legacy `tenantID`.
4
4
 
5
5
  ## 🚀 Quick Start (Server Mode)
6
6
 
@@ -20,14 +20,14 @@ curl -s -X POST http://localhost:8080/v1alpha1/mem9s \
20
20
  # {"id": "uuid"}
21
21
  ```
22
22
 
23
- Add mem9 to your project's `openclaw.json`:
23
+ Add mnemo to your project's `openclaw.json`:
24
24
 
25
25
  ```json
26
26
  {
27
27
  "plugins": {
28
- "slots": { "memory": "mem9" },
28
+ "slots": { "memory": "openclaw" },
29
29
  "entries": {
30
- "mem9": {
30
+ "openclaw": {
31
31
  "enabled": true,
32
32
  "config": {
33
33
  "apiUrl": "http://localhost:8080",
@@ -64,8 +64,8 @@ This is a `kind: "memory"` plugin — OpenClaw's framework manages when to load/
64
64
 
65
65
  | Hook | Trigger | What it does |
66
66
  |---|---|---|
67
- | `before_prompt_build` | Every LLM call | Searches memories by current prompt and injects relevant ones as context |
68
- | `after_compaction` | After `/compact` | Logs compaction so the next prompt re-queries memories from the server |
67
+ | `before_prompt_build` | Every LLM call | Searches memories by current prompt, injects relevant ones as context (3-min TTL cache) |
68
+ | `after_compaction` | After `/compact` | Invalidates cache so the next prompt gets fresh memories from the database |
69
69
  | `before_reset` | Before `/reset` | Saves a session summary (last 3 user messages) as memory before context is wiped |
70
70
  | `agent_end` | Agent finishes | Auto-captures the last assistant response as memory (if substantial) |
71
71
 
@@ -73,7 +73,7 @@ This is a `kind: "memory"` plugin — OpenClaw's framework manages when to load/
73
73
 
74
74
  | Tool | Description |
75
75
  |---|---|
76
- | `memory_store` | Store a new memory |
76
+ | `memory_store` | Store a new memory (upsert by key) |
77
77
  | `memory_search` | Hybrid vector + keyword search (or keyword-only) |
78
78
  | `memory_get` | Retrieve a single memory by ID |
79
79
  | `memory_update` | Update an existing memory |
@@ -91,7 +91,7 @@ This is a `kind: "memory"` plugin — OpenClaw's framework manages when to load/
91
91
  ### Method A: npm install (Recommended)
92
92
 
93
93
  ```bash
94
- openclaw plugins install @mem9/mem9
94
+ openclaw plugins install @mem9/openclaw
95
95
  ```
96
96
 
97
97
  ### Method B: From source
@@ -104,7 +104,7 @@ npm install
104
104
 
105
105
  ### Configure OpenClaw
106
106
 
107
- Add mem9 to your project's `openclaw.json`:
107
+ Add mnemo to your project's `openclaw.json`:
108
108
 
109
109
  OpenClaw is often deployed across teams with multiple agents. Server mode gives you:
110
110
 
@@ -139,10 +139,10 @@ Each agent uses the same `apiKey` for the shared memory pool. The plugin sends t
139
139
  {
140
140
  "plugins": {
141
141
  "slots": {
142
- "memory": "mem9"
142
+ "memory": "openclaw"
143
143
  },
144
144
  "entries": {
145
- "mem9": {
145
+ "openclaw": {
146
146
  "enabled": true,
147
147
  "config": {
148
148
  "apiUrl": "http://your-server:8080",
@@ -160,8 +160,8 @@ That's it. The server handles scoping and conflict resolution. Conceptually, the
160
160
 
161
161
  Start OpenClaw. You should see:
162
162
 
163
- ```text
164
- [mem9] Server mode (v1alpha2)
163
+ ```
164
+ [mem9] Server mode
165
165
  ```
166
166
 
167
167
  If you see `[mem9] No mode configured...`, check your `openclaw.json` config.
@@ -174,12 +174,11 @@ Defined in `openclaw.plugin.json`:
174
174
  |---|---|---|
175
175
  | `apiUrl` | string | mnemo-server URL |
176
176
  | `apiKey` | string | Preferred key. Uses `/v1alpha2/mem9s/...` with `X-API-Key` header |
177
- | `provisionQueryParams` | object | Optional `utm_*` map forwarded only to the initial `POST /v1alpha1/mem9s` auto-provision request when `apiKey` is absent |
178
177
  | `defaultTimeoutMs` | number | Default timeout for non-search mem9 API requests in milliseconds. Default: `8000` |
179
178
  | `searchTimeoutMs` | number | Timeout for `memory_search` and automatic recall search in milliseconds. Default: `15000` |
180
179
  | `tenantID` | string | Legacy alias for `apiKey`. The plugin still uses `/v1alpha2/mem9s/...` with `X-API-Key`. |
181
180
 
182
- > **Note**: `apiKey` takes precedence when both fields are set. If only `tenantID` is present, the plugin treats it as a legacy alias for `apiKey`, still uses v1alpha2, and logs a deprecation warning once at startup. `provisionQueryParams` is ignored after an `apiKey` is already configured, and non-`utm_*` keys are dropped before the provision request is sent.
181
+ > **Note**: `apiKey` takes precedence when both fields are set. If only `tenantID` is present, the plugin treats it as a legacy alias for `apiKey`, still uses v1alpha2, and logs a deprecation warning once at startup.
183
182
 
184
183
  ## Timeout Behavior
185
184
 
@@ -214,7 +213,7 @@ Example:
214
213
  openclaw-plugin/
215
214
  ├── README.md # This file
216
215
  ├── openclaw.plugin.json # Plugin metadata + config schema
217
- ├── package.json # npm package (@mem9/mem9)
216
+ ├── package.json # npm package (@mem9/openclaw)
218
217
  ├── index.ts # Plugin entry point + tool registration
219
218
  ├── backend.ts # MemoryBackend interface
220
219
  ├── server-backend.ts # Server mode: fetch → mnemo API
@@ -229,4 +228,4 @@ openclaw-plugin/
229
228
  | `No mode configured` | Missing config | Add `apiUrl` and `apiKey` (or legacy `tenantID`) to plugin config |
230
229
  | `Server mode requires...` | Missing key | Add `apiKey` (or legacy `tenantID`) to config |
231
230
  | Search requests time out | Hybrid/vector search exceeds plugin timeout | Increase `searchTimeoutMs` in plugin config |
232
- | Plugin not loading | Not in memory slot | Set `"slots": {"memory": "mem9"}` in openclaw.json |
231
+ | Plugin not loading | Not in memory slot | Set `"slots": {"memory": "openclaw"}` in openclaw.json |
package/index.ts CHANGED
@@ -314,15 +314,7 @@ const mnemoPlugin = {
314
314
  api.logger.info("[mem9] tenantID is deprecated; treating it as apiKey for v1alpha2");
315
315
  }
316
316
  const registerTenant = async (agentName: string): Promise<string> => {
317
- const backend = new ServerBackend(
318
- effectiveApiUrl,
319
- "",
320
- agentName,
321
- {
322
- timeouts: timeoutConfig,
323
- provisionQueryParams: cfg.provisionQueryParams ?? {},
324
- },
325
- );
317
+ const backend = new ServerBackend(effectiveApiUrl, "", agentName, timeoutConfig);
326
318
  const result = await backend.register();
327
319
  api.logger.info(
328
320
  `[mem9] *** Auto-provisioned apiKey=${result.id} *** Save this to your config as apiKey`
@@ -414,9 +406,7 @@ class LazyServerBackend implements MemoryBackend {
414
406
 
415
407
  this.resolving = this.apiKeyProvider().then((apiKey) =>
416
408
  Promise.resolve().then(() => {
417
- this.resolved = new ServerBackend(this.apiUrl, apiKey, this.agentId, {
418
- timeouts: this.timeouts,
419
- });
409
+ this.resolved = new ServerBackend(this.apiUrl, apiKey, this.agentId, this.timeouts);
420
410
  return this.resolved;
421
411
  })
422
412
  ).catch((err) => {
@@ -14,13 +14,6 @@
14
14
  "type": "string",
15
15
  "description": "mem9 API key (secret — do not share)"
16
16
  },
17
- "provisionQueryParams": {
18
- "type": "object",
19
- "description": "Optional utm_* params forwarded only during first-time auto-provisioning",
20
- "additionalProperties": {
21
- "type": "string"
22
- }
23
- },
24
17
  "defaultTimeoutMs": {
25
18
  "type": "number",
26
19
  "minimum": 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mem9/mem9",
3
- "version": "0.4.0-rc.0",
3
+ "version": "0.4.1",
4
4
  "description": "OpenClaw shared memory plugin — cloud-persistent memory with hybrid vector + keyword search via mnemo-server",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -34,7 +34,6 @@
34
34
  "access": "public"
35
35
  },
36
36
  "scripts": {
37
- "test": "node --experimental-strip-types --test ./server-backend.test.ts",
38
37
  "typecheck": "tsc --noEmit",
39
38
  "prepublishOnly": "npm run typecheck"
40
39
  },
@@ -43,7 +42,6 @@
43
42
  },
44
43
  "dependencies": {},
45
44
  "devDependencies": {
46
- "@types/node": "^22.15.30",
47
45
  "typescript": "^5.5.0"
48
46
  },
49
47
  "openclaw": {
package/server-backend.ts CHANGED
@@ -22,11 +22,6 @@ export interface BackendTimeouts {
22
22
  searchTimeoutMs?: number;
23
23
  }
24
24
 
25
- interface ServerBackendOptions {
26
- timeouts?: BackendTimeouts;
27
- provisionQueryParams?: Record<string, string>;
28
- }
29
-
30
25
  interface RequestOptions {
31
26
  timeoutMs?: number;
32
27
  }
@@ -35,37 +30,25 @@ export class ServerBackend implements MemoryBackend {
35
30
  private baseUrl: string;
36
31
  private apiKey: string;
37
32
  private agentName: string;
38
- private provisionQueryParams: Record<string, string>;
39
33
  private timeouts: Required<BackendTimeouts>;
40
34
 
41
35
  constructor(
42
36
  apiUrl: string,
43
37
  apiKey: string,
44
38
  agentName: string,
45
- options: ServerBackendOptions = {},
39
+ timeouts: BackendTimeouts = {},
46
40
  ) {
47
41
  this.baseUrl = apiUrl.replace(/\/+$/, "");
48
42
  this.apiKey = apiKey;
49
43
  this.agentName = agentName;
50
- this.provisionQueryParams = options.provisionQueryParams ?? {};
51
44
  this.timeouts = {
52
- defaultTimeoutMs: options.timeouts?.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS,
53
- searchTimeoutMs: options.timeouts?.searchTimeoutMs ?? DEFAULT_SEARCH_TIMEOUT_MS,
45
+ defaultTimeoutMs: timeouts.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS,
46
+ searchTimeoutMs: timeouts.searchTimeoutMs ?? DEFAULT_SEARCH_TIMEOUT_MS,
54
47
  };
55
48
  }
56
49
 
57
50
  async register(): Promise<ProvisionMem9sResponse> {
58
- const query = new URLSearchParams();
59
- for (const [key, value] of Object.entries(this.provisionQueryParams)) {
60
- if (!key.startsWith("utm_") || typeof value !== "string" || value === "") {
61
- continue;
62
- }
63
-
64
- query.set(key, value);
65
- }
66
-
67
- const qs = query.toString();
68
- const resp = await fetch(this.baseUrl + "/v1alpha1/mem9s" + (qs ? `?${qs}` : ""), {
51
+ const resp = await fetch(this.baseUrl + "/v1alpha1/mem9s", {
69
52
  method: "POST",
70
53
  signal: AbortSignal.timeout(this.timeouts.defaultTimeoutMs),
71
54
  });
package/types.ts CHANGED
@@ -2,7 +2,6 @@ export interface PluginConfig {
2
2
  // Server mode (apiUrl present → server)
3
3
  apiUrl?: string;
4
4
  apiKey?: string;
5
- provisionQueryParams?: Record<string, string>;
6
5
  tenantID?: string;
7
6
  defaultTimeoutMs?: number;
8
7
  searchTimeoutMs?: number;
@@ -1,82 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
-
4
- import { ServerBackend } from "./server-backend.ts";
5
-
6
- test("register forwards only utm_* params during auto-provision", async () => {
7
- const originalFetch = globalThis.fetch;
8
- let requestedURL = "";
9
-
10
- globalThis.fetch = async (input, init) => {
11
- requestedURL = String(input);
12
- assert.equal(init?.method, "POST");
13
-
14
- return new Response(JSON.stringify({ id: "space-1" }), {
15
- status: 201,
16
- headers: {
17
- "Content-Type": "application/json",
18
- },
19
- });
20
- };
21
-
22
- try {
23
- const backend = new ServerBackend("https://api.mem9.ai", "", "agent-1", {
24
- provisionQueryParams: {
25
- utm_source: "bosn",
26
- foo: "bar",
27
- utm_campaign: "spring",
28
- utm_medium: "",
29
- },
30
- });
31
-
32
- const result = await backend.register();
33
- assert.equal(result.id, "space-1");
34
-
35
- const url = new URL(requestedURL);
36
- assert.equal(url.origin + url.pathname, "https://api.mem9.ai/v1alpha1/mem9s");
37
- assert.equal(url.searchParams.get("utm_source"), "bosn");
38
- assert.equal(url.searchParams.get("utm_campaign"), "spring");
39
- assert.equal(url.searchParams.has("foo"), false);
40
- assert.equal(url.searchParams.has("utm_medium"), false);
41
- } finally {
42
- globalThis.fetch = originalFetch;
43
- }
44
- });
45
-
46
- test("normal memory requests do not append provision query params", async () => {
47
- const originalFetch = globalThis.fetch;
48
- let requestedURL = "";
49
-
50
- globalThis.fetch = async (input) => {
51
- requestedURL = String(input);
52
-
53
- return new Response(
54
- JSON.stringify({
55
- id: "mem-1",
56
- content: "remember this",
57
- created_at: "2026-04-05T00:00:00Z",
58
- updated_at: "2026-04-05T00:00:00Z",
59
- }),
60
- {
61
- status: 200,
62
- headers: {
63
- "Content-Type": "application/json",
64
- },
65
- },
66
- );
67
- };
68
-
69
- try {
70
- const backend = new ServerBackend("https://api.mem9.ai", "space-key", "agent-1", {
71
- provisionQueryParams: {
72
- utm_source: "bosn",
73
- },
74
- });
75
-
76
- await backend.store({ content: "remember this" });
77
-
78
- assert.equal(requestedURL, "https://api.mem9.ai/v1alpha2/mem9s/memories");
79
- } finally {
80
- globalThis.fetch = originalFetch;
81
- }
82
- });