openclaw-amem 1.2.0 → 1.2.2
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 +11 -2
- package/dist/index.js +17 -44
- package/openclaw.plugin.json +15 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ The first open-source A-MEM memory plugin for OpenClaw: dynamic graph linking, h
|
|
|
33
33
|
- OpenClaw v2026.4+
|
|
34
34
|
- Node.js 24 (18+ works; 24/26 supported)
|
|
35
35
|
- Qdrant running on `:6333`
|
|
36
|
-
-
|
|
36
|
+
- An LLM: `ANTHROPIC_API_KEY` by default, or any OpenAI-compatible provider — see [LLM provider](#llm-provider)
|
|
37
37
|
|
|
38
38
|
## Installation
|
|
39
39
|
|
|
@@ -83,6 +83,15 @@ Add `openclaw-amem` to your allowed plugins and hook it into the `memory` slot:
|
|
|
83
83
|
openclaw gateway restart
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
## LLM provider
|
|
87
|
+
|
|
88
|
+
The plugin calls an LLM for note construction, linking, and evolution. Pick the backend with `AMEM_LLM_PROVIDER`:
|
|
89
|
+
|
|
90
|
+
- **`anthropic`** (default) — the Anthropic Messages API. Set `ANTHROPIC_API_KEY`.
|
|
91
|
+
- **`openai`** — the OpenAI Chat Completions API, which every OpenAI-compatible endpoint speaks. Set `AMEM_LLM_PROVIDER=openai`, point `AMEM_LLM_BASE_URL` at the endpoint, and set `AMEM_LLM_API_KEY` (or the standard `OPENAI_API_KEY`). Covers **OpenAI, DeepSeek, OpenRouter, Groq, Together**, and local servers (**Ollama, vLLM, LM Studio** — no key needed). Reasoning models (`o1`, `o3`, `gpt-5`) are handled automatically.
|
|
92
|
+
|
|
93
|
+
Choose the model with `AMEM_LLM_MODEL`. Full env-var reference and examples: **[amem.owo.lc/reference/configuration](https://amem.owo.lc/reference/configuration)**.
|
|
94
|
+
|
|
86
95
|
## Configuration Reference
|
|
87
96
|
|
|
88
97
|
| Key | Type | Default | Description |
|
|
@@ -110,7 +119,7 @@ A memory plugin's job is to read configuration from the environment and send mem
|
|
|
110
119
|
|
|
111
120
|
What it actually does — all of it declared in [`openclaw.plugin.json`](openclaw.plugin.json):
|
|
112
121
|
|
|
113
|
-
- **Environment variables it reads** (its configuration surface, supplied by you): `AMEM_LLM_PROVIDER`, `AMEM_LLM_API_KEY`, `AMEM_LLM_BASE_URL`, `AMEM_LLM_MODEL`, `AMEM_COLLECTION`, `AMEM_DATA_DIR`, `AMEM_EVO_COUNTER_PATH`, `AMEM_REVIEW_DIR`, `AMEM_PROMPT_LOCALE`. No credential is bundled, hardcoded, or logged.
|
|
122
|
+
- **Environment variables it reads** (its configuration surface, supplied by you): `AMEM_LLM_PROVIDER`, `AMEM_LLM_API_KEY`, `OPENAI_API_KEY`, `AMEM_LLM_BASE_URL`, `AMEM_LLM_MODEL`, `AMEM_COLLECTION`, `AMEM_DATA_DIR`, `AMEM_EVO_COUNTER_PATH`, `AMEM_REVIEW_DIR`, `AMEM_PROMPT_LOCALE`. No credential is bundled, hardcoded, or logged.
|
|
114
123
|
- **Network destinations**: only your **local Qdrant** (`http://localhost:6333`) and your configured **LLM endpoint** (Anthropic by default, or any OpenAI-compatible endpoint via `AMEM_LLM_PROVIDER=openai` + `AMEM_LLM_BASE_URL`). It sends memory text/embeddings there to store and evolve notes — its stated purpose. It does not phone home.
|
|
115
124
|
- **Conversation content** is processed for memory only when you set `hooks.allowConversationAccess: true`. Keep Qdrant and review-output paths scoped to locations you control.
|
|
116
125
|
|
package/dist/index.js
CHANGED
|
@@ -5,8 +5,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __esm = (fn, res) => function __init() {
|
|
9
|
-
|
|
8
|
+
var __esm = (fn, res, err) => function __init() {
|
|
9
|
+
if (err) throw err[0];
|
|
10
|
+
try {
|
|
11
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
throw err = [e], e;
|
|
14
|
+
}
|
|
10
15
|
};
|
|
11
16
|
var __export = (target, all) => {
|
|
12
17
|
for (var name in all)
|
|
@@ -1838,12 +1843,9 @@ __export(index_exports, {
|
|
|
1838
1843
|
ensureCollection: () => ensureCollection,
|
|
1839
1844
|
generateReviewBatch: () => generateReviewBatch,
|
|
1840
1845
|
getNote: () => getNote,
|
|
1841
|
-
hookLiveness: () => hookLiveness,
|
|
1842
|
-
hookNeverFiredWarning: () => hookNeverFiredWarning,
|
|
1843
1846
|
invalidateNote: () => invalidateNote,
|
|
1844
1847
|
listMemories: () => listMemories,
|
|
1845
1848
|
listNotes: () => listNotes,
|
|
1846
|
-
markHookFired: () => markHookFired,
|
|
1847
1849
|
mergeSimilarNotes: () => mergeSimilarNotes,
|
|
1848
1850
|
patchNotePayload: () => patchNotePayload,
|
|
1849
1851
|
register: () => register,
|
|
@@ -1858,29 +1860,12 @@ var import_plugin_entry = require("openclaw/plugin-sdk/plugin-entry");
|
|
|
1858
1860
|
init_src();
|
|
1859
1861
|
var import_crypto2 = require("crypto");
|
|
1860
1862
|
|
|
1861
|
-
// src/
|
|
1862
|
-
var
|
|
1863
|
-
var
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
let s = g[HOOK_STATE_KEY];
|
|
1868
|
-
if (!s) {
|
|
1869
|
-
s = { everFired: false, lastFiredAt: 0, firstSeenAt: now };
|
|
1870
|
-
g[HOOK_STATE_KEY] = s;
|
|
1871
|
-
}
|
|
1872
|
-
return s;
|
|
1873
|
-
}
|
|
1874
|
-
function markHookFired(now = Date.now()) {
|
|
1875
|
-
const s = hookLiveness(now);
|
|
1876
|
-
s.everFired = true;
|
|
1877
|
-
s.lastFiredAt = now;
|
|
1878
|
-
}
|
|
1879
|
-
function hookNeverFiredWarning(now = Date.now()) {
|
|
1880
|
-
const s = hookLiveness(now);
|
|
1881
|
-
if (s.everFired) return "";
|
|
1882
|
-
if (now - s.firstSeenAt <= HOOK_WARN_DELAY_MS) return "";
|
|
1883
|
-
return HOOK_WARNING_TEXT;
|
|
1863
|
+
// src/conv-access.ts
|
|
1864
|
+
var BLOCKED_WARNING_LOG = "openclaw-amem: \u26A0\uFE0F automatic memory write-back is OFF \u2014 the agent_end hook is blocked by OpenClaw's security policy, so conversations are NOT being saved to long-term memory. Enable it: set plugins.entries.openclaw-amem.hooks.allowConversationAccess=true in openclaw.json, then restart the gateway.";
|
|
1865
|
+
var BLOCKED_WARNING_SUFFIX = "\n\n\u26A0\uFE0F NOTE TO ASSISTANT \u2014 please tell the user: openclaw-amem automatic memory write-back is currently DISABLED, so nothing from this conversation is being saved to long-term memory. To fix it, add `plugins.entries.openclaw-amem.hooks.allowConversationAccess: true` to openclaw.json and restart the gateway.";
|
|
1866
|
+
function isConvAccessBlocked(config, pluginId) {
|
|
1867
|
+
const entry = config?.plugins?.entries?.[pluginId];
|
|
1868
|
+
return entry !== void 0 && entry.hooks?.allowConversationAccess !== true;
|
|
1884
1869
|
}
|
|
1885
1870
|
|
|
1886
1871
|
// src/scope.ts
|
|
@@ -1910,9 +1895,11 @@ init_src();
|
|
|
1910
1895
|
var _config = {};
|
|
1911
1896
|
function register(api) {
|
|
1912
1897
|
const logger = api.logger;
|
|
1913
|
-
hookLiveness();
|
|
1914
1898
|
_config = api.pluginConfig || {};
|
|
1915
1899
|
const pluginConfig = _config;
|
|
1900
|
+
const pluginId = api.id ?? "openclaw-amem";
|
|
1901
|
+
const convBlocked = isConvAccessBlocked(api.config, pluginId);
|
|
1902
|
+
if (convBlocked) logger.warn(BLOCKED_WARNING_LOG);
|
|
1916
1903
|
configure({ dataDir: path5.join(os2.homedir(), ".openclaw") });
|
|
1917
1904
|
const resolveAgentId2 = (ctx) => resolveAgentId(ctx, pluginConfig);
|
|
1918
1905
|
const buildScope2 = (rawAgentId) => buildScope(rawAgentId, pluginConfig, createStorageContext);
|
|
@@ -2021,7 +2008,7 @@ function register(api) {
|
|
|
2021
2008
|
async execute(_toolCallId, params) {
|
|
2022
2009
|
const { query, limit = 5, topicsFilter } = params;
|
|
2023
2010
|
const start = Date.now();
|
|
2024
|
-
const hookWarning =
|
|
2011
|
+
const hookWarning = convBlocked ? BLOCKED_WARNING_SUFFIX : "";
|
|
2025
2012
|
try {
|
|
2026
2013
|
const results = await searchMemory(query, limit, scope.agentId, {
|
|
2027
2014
|
topicsFilter,
|
|
@@ -2203,7 +2190,6 @@ ${text}${hookWarning}` }],
|
|
|
2203
2190
|
hookFn(
|
|
2204
2191
|
"agent_end",
|
|
2205
2192
|
async (event, ctx) => {
|
|
2206
|
-
markHookFired();
|
|
2207
2193
|
const scope = buildScope2(resolveAgentId2(ctx));
|
|
2208
2194
|
const agentId = scope.agentId;
|
|
2209
2195
|
const storageCtx = scope.storageCtx;
|
|
@@ -2289,16 +2275,6 @@ ${mergeErr.stack}`
|
|
|
2289
2275
|
{ timeoutMs: 3e4 }
|
|
2290
2276
|
);
|
|
2291
2277
|
logger.info("openclaw-amem: agent_end CRUD decision hook registered");
|
|
2292
|
-
setTimeout(
|
|
2293
|
-
() => {
|
|
2294
|
-
if (!hookLiveness().everFired) {
|
|
2295
|
-
logger.warn(
|
|
2296
|
-
"\u26A0\uFE0F openclaw-amem: agent_end hook has never fired in 10 minutes. It may be blocked by OpenClaw security policy. Add to openclaw.json: plugins.entries.openclaw-amem.hooks.allowConversationAccess=true"
|
|
2297
|
-
);
|
|
2298
|
-
}
|
|
2299
|
-
},
|
|
2300
|
-
10 * 60 * 1e3
|
|
2301
|
-
);
|
|
2302
2278
|
}
|
|
2303
2279
|
function scheduleNextRun() {
|
|
2304
2280
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -2352,12 +2328,9 @@ var index_default = plugin;
|
|
|
2352
2328
|
ensureCollection,
|
|
2353
2329
|
generateReviewBatch,
|
|
2354
2330
|
getNote,
|
|
2355
|
-
hookLiveness,
|
|
2356
|
-
hookNeverFiredWarning,
|
|
2357
2331
|
invalidateNote,
|
|
2358
2332
|
listMemories,
|
|
2359
2333
|
listNotes,
|
|
2360
|
-
markHookFired,
|
|
2361
2334
|
mergeSimilarNotes,
|
|
2362
2335
|
patchNotePayload,
|
|
2363
2336
|
register,
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
"id": "openclaw-amem",
|
|
3
3
|
"name": "Memory (A-MEM v2)",
|
|
4
4
|
"description": "OpenClaw memory plugin implementing A-MEM — memories evolve, not just accumulate. Graph linking, hybrid retrieval, LLM-driven evolution. No Python.",
|
|
5
|
-
"version": "1.2.
|
|
5
|
+
"version": "1.2.2",
|
|
6
6
|
"kind": "memory",
|
|
7
7
|
"openclaw": {
|
|
8
8
|
"compat": {
|
|
9
9
|
"pluginApi": ">=2026.3.24"
|
|
10
10
|
},
|
|
11
11
|
"build": {
|
|
12
|
-
"openclawVersion": "2026.
|
|
12
|
+
"openclawVersion": "2026.7.1-2"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"contracts": {
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
{
|
|
27
27
|
"id": "amem",
|
|
28
28
|
"envVars": [
|
|
29
|
+
"AMEM_LLM_PROVIDER",
|
|
29
30
|
"AMEM_LLM_API_KEY",
|
|
31
|
+
"OPENAI_API_KEY",
|
|
30
32
|
"AMEM_LLM_BASE_URL",
|
|
31
33
|
"AMEM_LLM_MODEL",
|
|
32
34
|
"AMEM_COLLECTION",
|
|
@@ -57,7 +59,17 @@
|
|
|
57
59
|
"hostSuffixes": [
|
|
58
60
|
".anthropic.com"
|
|
59
61
|
],
|
|
60
|
-
"note": "LLM for note construction / link generation / evolution; override the base URL via AMEM_LLM_BASE_URL."
|
|
62
|
+
"note": "Default LLM for note construction / link generation / evolution; override the base URL via AMEM_LLM_BASE_URL."
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"endpointClass": "openai",
|
|
66
|
+
"hosts": [
|
|
67
|
+
"api.openai.com"
|
|
68
|
+
],
|
|
69
|
+
"hostSuffixes": [
|
|
70
|
+
".openai.com"
|
|
71
|
+
],
|
|
72
|
+
"note": "Alternate LLM when AMEM_LLM_PROVIDER=openai — the OpenAI Chat Completions API, or any compatible gateway (DeepSeek, OpenRouter, Groq, Together, Ollama, vLLM) selected via AMEM_LLM_BASE_URL."
|
|
61
73
|
}
|
|
62
74
|
],
|
|
63
75
|
"configSchema": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-amem",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "OpenClaw memory plugin implementing A-MEM — memories evolve, not just accumulate. Graph linking, hybrid retrieval, LLM-driven evolution. No Python.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"pluginApi": ">=2026.3.24"
|
|
13
13
|
},
|
|
14
14
|
"build": {
|
|
15
|
-
"openclawVersion": "2026.
|
|
15
|
+
"openclawVersion": "2026.7.1-2"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|