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 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
- - Anthropic API key (`ANTHROPIC_API_KEY`) or set `AMEM_LLM_BASE_URL` for a compatible proxy
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
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
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/hook-liveness.ts
1862
- var HOOK_STATE_KEY = /* @__PURE__ */ Symbol.for("openclaw-amem.hookLiveness");
1863
- var HOOK_WARN_DELAY_MS = 10 * 60 * 1e3;
1864
- var HOOK_WARNING_TEXT = "\n\n\u26A0\uFE0F Warning: agent_end hook has never fired. Automatic memory write-back may be disabled. Set plugins.entries.openclaw-amem.hooks.allowConversationAccess=true in openclaw.json.";
1865
- function hookLiveness(now = Date.now()) {
1866
- const g = globalThis;
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 = hookNeverFiredWarning();
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,
@@ -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.0",
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.6.9"
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.0",
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.6.9"
15
+ "openclawVersion": "2026.7.1-2"
16
16
  }
17
17
  },
18
18
  "dependencies": {