openclaw-amem 1.1.3 → 1.1.5

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/dist/index.js CHANGED
@@ -30,56 +30,24 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  ));
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
- // ../amem-core/dist/index.js
34
- var dist_exports = {};
35
- __export(dist_exports, {
36
- addMemory: () => addMemory,
37
- addNote: () => addNote,
38
- checkQuality: () => checkQuality,
39
- configure: () => configure,
40
- consolidateMemories: () => consolidateMemories,
41
- cosineSimilarity: () => cosineSimilarity,
42
- countNotes: () => countNotes,
43
- createStorageContext: () => createStorageContext,
44
- deleteNote: () => deleteNote,
45
- encode: () => encode,
46
- ensureCollection: () => ensureCollection,
47
- findByHash: () => findByHash,
48
- generateReviewBatch: () => generateReviewBatch,
49
- getDataDir: () => getDataDir,
50
- getEvoCount: () => getEvoCount,
51
- getNote: () => getNote,
52
- getNotesByDatePrefix: () => getNotesByDatePrefix,
53
- incrementEvoCount: () => incrementEvoCount,
54
- invalidateNote: () => invalidateNote,
55
- listMemories: () => listMemories,
56
- listNotes: () => listNotes,
57
- llmCall: () => llmCall,
58
- llmConstructNote: () => llmConstructNote,
59
- llmCrudDecision: () => llmCrudDecision,
60
- llmEvolutionJudge: () => llmEvolutionJudge,
61
- llmEvolveNote: () => llmEvolveNote,
62
- llmShouldLink: () => llmShouldLink,
63
- llmShouldMerge: () => llmShouldMerge,
64
- mergeSimilarNotes: () => mergeSimilarNotes,
65
- patchNotePayload: () => patchNotePayload,
66
- queryByEmbedding: () => queryByEmbedding,
67
- replaceLinkReferences: () => replaceLinkReferences,
68
- resetCollectionReady: () => resetCollectionReady,
69
- scanLowQuality: () => scanLowQuality,
70
- searchMemory: () => searchMemory,
71
- shouldRunEvolution: () => shouldRunEvolution,
72
- t: () => t,
73
- updateNote: () => updateNote,
74
- updateNoteContent: () => updateNoteContent,
75
- updateNoteLinks: () => updateNoteLinks
76
- });
33
+ // ../amem-core/src/config.ts
77
34
  function configure(opts) {
78
35
  if (opts.dataDir) _dataDir = opts.dataDir;
79
36
  }
80
37
  function getDataDir() {
81
38
  return _dataDir;
82
39
  }
40
+ var os, path, _dataDir;
41
+ var init_config = __esm({
42
+ "../amem-core/src/config.ts"() {
43
+ "use strict";
44
+ os = __toESM(require("os"), 1);
45
+ path = __toESM(require("path"), 1);
46
+ _dataDir = process.env.AMEM_DATA_DIR || path.join(os.homedir(), ".amem");
47
+ }
48
+ });
49
+
50
+ // ../amem-core/src/embedding.ts
83
51
  async function getExtractor() {
84
52
  if (extractor) return extractor;
85
53
  if (!pipeline) {
@@ -133,11 +101,28 @@ async function encode(text) {
133
101
  }
134
102
  throw new Error("Unexpected embedding output shape");
135
103
  }
104
+ async function loadModel() {
105
+ await getExtractor();
106
+ }
107
+ function isModelLoaded() {
108
+ return extractor !== null;
109
+ }
136
110
  function cosineSimilarity(a, b) {
137
111
  let dot = 0;
138
112
  for (let i = 0; i < a.length; i++) dot += a[i] * b[i];
139
113
  return dot;
140
114
  }
115
+ var pipeline, extractor, MODEL_NAME;
116
+ var init_embedding = __esm({
117
+ "../amem-core/src/embedding.ts"() {
118
+ "use strict";
119
+ pipeline = null;
120
+ extractor = null;
121
+ MODEL_NAME = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
122
+ }
123
+ });
124
+
125
+ // ../amem-core/src/evo-counter.ts
141
126
  function counterFile() {
142
127
  return process.env.AMEM_EVO_COUNTER_PATH || path2.join(getDataDir(), "amem_evo_cnt.json");
143
128
  }
@@ -158,6 +143,199 @@ function shouldRunEvolution() {
158
143
  const count = incrementEvoCount();
159
144
  return count % EVO_THRESHOLD === 0;
160
145
  }
146
+ var fs, path2, EVO_THRESHOLD;
147
+ var init_evo_counter = __esm({
148
+ "../amem-core/src/evo-counter.ts"() {
149
+ "use strict";
150
+ fs = __toESM(require("fs"), 1);
151
+ path2 = __toESM(require("path"), 1);
152
+ init_config();
153
+ EVO_THRESHOLD = 20;
154
+ }
155
+ });
156
+
157
+ // ../amem-core/src/prompts.ts
158
+ var LOCALE, en, zh, templates, t;
159
+ var init_prompts = __esm({
160
+ "../amem-core/src/prompts.ts"() {
161
+ "use strict";
162
+ LOCALE = process.env.AMEM_PROMPT_LOCALE === "zh" ? "zh" : "en";
163
+ en = {
164
+ crudDecision: (userText, assistantText, memoryList) => `You are a memory management agent. Analyze the conversation and decide what memory operations are needed.
165
+
166
+ ## Conversation
167
+
168
+ User: ${userText}
169
+ Assistant: ${assistantText}
170
+
171
+ ## Existing relevant memories (identified by integer idx)
172
+
173
+ ${memoryList}
174
+
175
+ ## Task
176
+
177
+ Extract only genuinely important long-term facts (decisions, preferences, account info, project status, key insights). Skip small talk, confirmations, and information already captured in existing memories.
178
+
179
+ ## Operation types
180
+ - NEW: Extract a brand new fact not present in existing memories
181
+ - UPDATE: New information refines or supersedes an existing memory; specify existingIdx
182
+ - DELETE: An existing memory is outdated, contradicted, or wrong; specify existingIdx, fact = original content
183
+ - NONE: Nothing worth recording, or information already fully captured
184
+
185
+ ## Output format
186
+
187
+ Return a JSON array. Each item:
188
+ {"action": "NEW"|"UPDATE"|"DELETE"|"NONE", "fact": "fact content", "existingIdx": integer or omit, "reason": "optional"}
189
+
190
+ Return at most 3 operations. If nothing is worth recording, return [].
191
+ Return only the JSON array, no other text.
192
+
193
+ Examples:
194
+
195
+ 1. New preference:
196
+ [{"action": "NEW", "fact": "User prefers TypeScript over JavaScript", "reason": "Explicitly stated tech preference"}]
197
+
198
+ 2. Updating an existing memory (idx 0 was "User is evaluating React and Vue"):
199
+ [{"action": "UPDATE", "fact": "User decided to use React (dropped Vue)", "existingIdx": 0, "reason": "Decision finalized, update evaluation status"}]
200
+
201
+ 3. Conversation is just "Sure, thanks" / "Got it" with no new info:
202
+ []`,
203
+ shouldMerge: (contentA, contentB) => `You are a memory deduplication assistant. Determine whether two memories express essentially the same information.
204
+
205
+ Memory A: ${contentA}
206
+ Memory B: ${contentB}
207
+
208
+ Rules:
209
+ - If both memories express the same core fact (possibly different wording or granularity), return:
210
+ {"shouldMerge": true, "merged": "Concise merged statement preserving key details from both, more complete than either alone"}
211
+ - If the memories are complementary, on different topics, or contain different specific facts, return:
212
+ {"shouldMerge": false}
213
+
214
+ Return only JSON, no other text.
215
+
216
+ Examples:
217
+
218
+ 1. Should merge (different granularity):
219
+ A: "Project uses PostgreSQL"
220
+ B: "Project's primary database is PostgreSQL 16, deployed on AWS RDS"
221
+ -> {"shouldMerge": true, "merged": "Project uses PostgreSQL 16 as primary database, deployed on AWS RDS"}
222
+
223
+ 2. Should NOT merge (complementary but distinct):
224
+ A: "User prefers VS Code"
225
+ B: "User's VS Code uses One Dark Pro theme"
226
+ -> {"shouldMerge": false}`,
227
+ evolutionJudge: (oldContent, newContent) => `You are a memory evolution judge. Analyze the relationship between an old and new memory and return JSON.
228
+
229
+ Old memory: ${oldContent}
230
+ New memory: ${newContent}
231
+
232
+ Classification rules:
233
+
234
+ - EVOLVE: New content deepens or updates the old memory (e.g. "Considering Next.js" -> "Decided on Next.js 14 App Router")
235
+ Return: {"type": "EVOLVE", "mergedContent": "Merged content preserving the evolution trajectory"}
236
+
237
+ - CONFLICT: Old and new information directly contradict each other on the same attribute (e.g. "Uses MySQL as primary DB" vs "Migrated to PostgreSQL")
238
+ Return: {"type": "CONFLICT"}
239
+
240
+ - EXPAND: New information supplements the old memory on the same topic (e.g. "Handles backend dev" + "Backend uses Go and gRPC")
241
+ Return: {"type": "EXPAND", "mergedContent": "Merged content integrating both pieces of information"}
242
+
243
+ - NEW: Completely unrelated information, no substantive connection to the old memory
244
+ Return: {"type": "NEW"}
245
+
246
+ Return only JSON, no other text.`
247
+ };
248
+ zh = {
249
+ crudDecision: (userText, assistantText, memoryList) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u7BA1\u7406 agent\uFF0C\u8D1F\u8D23\u5206\u6790\u5BF9\u8BDD\u5185\u5BB9\u5E76\u51B3\u5B9A\u5982\u4F55\u64CD\u4F5C\u8BB0\u5FC6\u5E93\u3002
250
+
251
+ ## \u5BF9\u8BDD\u5185\u5BB9
252
+
253
+ \u7528\u6237\uFF1A${userText}
254
+ \u52A9\u624B\uFF1A${assistantText}
255
+
256
+ ## \u5DF2\u6709\u76F8\u5173\u8BB0\u5FC6\uFF08\u7528\u6574\u6570 idx \u6807\u8BC6\uFF09
257
+
258
+ ${memoryList}
259
+
260
+ ## \u4EFB\u52A1
261
+
262
+ \u5206\u6790\u4E0A\u8FF0\u5BF9\u8BDD\uFF0C\u51B3\u5B9A\u9700\u8981\u54EA\u4E9B\u8BB0\u5FC6\u64CD\u4F5C\u3002\u53EA\u63D0\u53D6\u771F\u6B63\u91CD\u8981\u7684\u957F\u671F\u4E8B\u5B9E\uFF08\u51B3\u7B56\u3001\u504F\u597D\u3001\u8D26\u53F7\u4FE1\u606F\u3001\u9879\u76EE\u72B6\u6001\u3001\u5173\u952E\u6D1E\u5BDF\uFF09\u3002\u8DF3\u8FC7\u95F2\u804A\u3001\u786E\u8BA4\u8BED\u3001\u91CD\u590D\u4FE1\u606F\u3002
263
+
264
+ ## \u64CD\u4F5C\u7C7B\u578B
265
+ - NEW\uFF1A\u63D0\u53D6\u5168\u65B0\u4E8B\u5B9E\uFF08\u5DF2\u6709\u8BB0\u5FC6\u4E2D\u6CA1\u6709\u7684\u4FE1\u606F\uFF09
266
+ - UPDATE\uFF1A\u65B0\u4FE1\u606F\u66F4\u65B0\u4E86\u67D0\u6761\u5DF2\u6709\u8BB0\u5FC6\uFF0C\u7528 existingIdx \u6307\u5B9A\u8981\u66F4\u65B0\u7684\u6761\u76EE
267
+ - DELETE\uFF1A\u67D0\u6761\u5DF2\u6709\u8BB0\u5FC6\u5DF2\u7ECF\u8FC7\u65F6\u3001\u53D1\u751F\u51B2\u7A81\u6216\u9519\u8BEF\uFF0C\u7528 existingIdx \u6307\u5B9A\uFF0Cfact \u586B\u539F\u5185\u5BB9
268
+ - NONE\uFF1A\u4E0D\u503C\u5F97\u8BB0\u5F55\u6216\u5DF2\u6709\u5B8C\u5168\u76F8\u540C\u7684\u4FE1\u606F
269
+
270
+ ## \u8F93\u51FA\u683C\u5F0F
271
+
272
+ \u8FD4\u56DE JSON \u6570\u7EC4\uFF0C\u6BCF\u6761\u683C\u5F0F\uFF1A
273
+ {"action": "NEW"|"UPDATE"|"DELETE"|"NONE", "fact": "\u4E8B\u5B9E\u5185\u5BB9", "existingIdx": \u6574\u6570\u6216\u7701\u7565, "reason": "\u539F\u56E0\uFF08\u53EF\u9009\uFF09"}
274
+
275
+ \u6BCF\u6B21\u6700\u591A\u8FD4\u56DE 3 \u6761\u64CD\u4F5C\u3002\u5982\u679C\u6CA1\u6709\u503C\u5F97\u64CD\u4F5C\u7684\u5185\u5BB9\uFF0C\u8FD4\u56DE []\u3002
276
+ \u53EA\u8FD4\u56DE JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002
277
+
278
+ \u793A\u4F8B\uFF1A
279
+
280
+ 1. \u63D0\u53D6\u65B0\u504F\u597D\uFF1A
281
+ [{"action": "NEW", "fact": "\u7528\u6237\u504F\u597D TypeScript \u800C\u975E JavaScript", "reason": "\u660E\u786E\u8868\u8FBE\u7684\u6280\u672F\u504F\u597D"}]
282
+
283
+ 2. \u66F4\u65B0\u5DF2\u6709\u8BB0\u5FC6\uFF08idx 0 \u539F\u4E3A"\u7528\u6237\u6B63\u5728\u8BC4\u4F30 React \u548C Vue"\uFF09\uFF1A
284
+ [{"action": "UPDATE", "fact": "\u7528\u6237\u51B3\u5B9A\u4F7F\u7528 React\uFF08\u653E\u5F03\u4E86 Vue\uFF09", "existingIdx": 0, "reason": "\u51B3\u7B56\u5DF2\u660E\u786E\uFF0C\u66F4\u65B0\u8BC4\u4F30\u72B6\u6001"}]
285
+
286
+ 3. \u5BF9\u8BDD\u4EC5\u4E3A"\u597D\u7684\uFF0C\u8C22\u8C22"/"\u6CA1\u95EE\u9898"\u7B49\u786E\u8BA4\u8BED\uFF0C\u65E0\u65B0\u4FE1\u606F\uFF1A
287
+ []`,
288
+ shouldMerge: (contentA, contentB) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u53BB\u91CD\u52A9\u624B\uFF0C\u8D1F\u8D23\u5224\u65AD\u4E24\u6761\u8BB0\u5FC6\u662F\u5426\u8868\u8FBE\u4E86\u672C\u8D28\u76F8\u540C\u7684\u4FE1\u606F\u3002
289
+
290
+ \u8BB0\u5FC6A\uFF1A${contentA}
291
+ \u8BB0\u5FC6B\uFF1A${contentB}
292
+
293
+ \u5224\u65AD\u89C4\u5219\uFF1A
294
+ - \u5982\u679C\u4E24\u6761\u8BB0\u5FC6\u8868\u8FBE\u7684\u662F\u672C\u8D28\u76F8\u540C\u7684\u4FE1\u606F\uFF08\u53EF\u80FD\u63AA\u8F9E\u4E0D\u540C\u3001\u7C92\u5EA6\u4E0D\u540C\uFF0C\u4F46\u6838\u5FC3\u4E8B\u5B9E\u4E00\u81F4\uFF09\uFF0C\u8FD4\u56DE JSON\uFF1A
295
+ {"shouldMerge": true, "merged": "\u5408\u5E76\u540E\u7684\u7B80\u6D01\u8868\u8FF0\uFF0C\u4FDD\u7559\u4E24\u6761\u8BB0\u5FC6\u7684\u5173\u952E\u4FE1\u606F\uFF0C\u6BD4\u4EFB\u4F55\u4E00\u6761\u90FD\u66F4\u5B8C\u6574"}
296
+ - \u5982\u679C\u4E24\u6761\u8BB0\u5FC6\u662F\u4E92\u8865\u4FE1\u606F\u3001\u4E0D\u540C\u4E3B\u9898\u3001\u6216\u5305\u542B\u4E0D\u540C\u7684\u5177\u4F53\u4E8B\u5B9E\uFF0C\u8FD4\u56DE JSON\uFF1A
297
+ {"shouldMerge": false}
298
+
299
+ \u53EA\u8FD4\u56DE JSON\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002
300
+
301
+ \u793A\u4F8B\uFF1A
302
+
303
+ 1. \u5E94\u5408\u5E76\uFF08\u7C92\u5EA6\u4E0D\u540C\uFF09\uFF1A
304
+ A: "\u9879\u76EE\u4F7F\u7528 PostgreSQL \u6570\u636E\u5E93"
305
+ B: "\u9879\u76EE\u7684\u4E3B\u6570\u636E\u5E93\u662F PostgreSQL 16\uFF0C\u90E8\u7F72\u5728 AWS RDS \u4E0A"
306
+ \u2192 {"shouldMerge": true, "merged": "\u9879\u76EE\u4F7F\u7528 PostgreSQL 16 \u4F5C\u4E3A\u4E3B\u6570\u636E\u5E93\uFF0C\u90E8\u7F72\u5728 AWS RDS \u4E0A"}
307
+
308
+ 2. \u4E0D\u5E94\u5408\u5E76\uFF08\u4E92\u8865\u4F46\u4E0D\u540C\uFF09\uFF1A
309
+ A: "\u7528\u6237\u559C\u6B22\u7528 VS Code"
310
+ B: "\u7528\u6237\u7684 VS Code \u4F7F\u7528 One Dark Pro \u4E3B\u9898"
311
+ \u2192 {"shouldMerge": false}`,
312
+ evolutionJudge: (oldContent, newContent) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u6F14\u5316\u5224\u65AD\u52A9\u624B\u3002\u5206\u6790\u4EE5\u4E0B\u4E24\u6761\u8BB0\u5FC6\u7684\u5173\u7CFB\u5E76\u8FD4\u56DE JSON\u3002
313
+
314
+ \u65E7\u8BB0\u5FC6\uFF1A${oldContent}
315
+ \u65B0\u8BB0\u5FC6\uFF1A${newContent}
316
+
317
+ \u5224\u65AD\u89C4\u5219\uFF1A
318
+
319
+ - EVOLVE\uFF1A\u65B0\u5185\u5BB9\u662F\u5BF9\u65E7\u8BB0\u5FC6\u7684\u6DF1\u5316/\u66F4\u65B0\uFF08\u5982\u300C\u6B63\u5728\u8003\u8651\u7528 Next.js\u300D\u2192\u300C\u51B3\u5B9A\u7528 Next.js 14 App Router\u300D\uFF09
320
+ \u8FD4\u56DE\uFF1A{"type": "EVOLVE", "mergedContent": "\u878D\u5408\u540E\u7684\u5B8C\u6574\u5185\u5BB9\uFF0C\u4FDD\u7559\u6F14\u5316\u8F68\u8FF9"}
321
+
322
+ - CONFLICT\uFF1A\u65B0\u65E7\u4FE1\u606F\u5728\u540C\u4E00\u5C5E\u6027\u4E0A\u76F4\u63A5\u77DB\u76FE\uFF08\u5982\u300C\u4F7F\u7528 MySQL \u4F5C\u4E3A\u4E3B\u6570\u636E\u5E93\u300Dvs\u300C\u5DF2\u8FC1\u79FB\u5230 PostgreSQL\u300D\uFF09
323
+ \u8FD4\u56DE\uFF1A{"type": "CONFLICT"}
324
+
325
+ - EXPAND\uFF1A\u65B0\u4FE1\u606F\u662F\u5BF9\u65E7\u8BB0\u5FC6\u540C\u4E00\u4E3B\u9898\u7684\u8865\u5145\u6269\u5C55\uFF08\u5982\u300C\u8D1F\u8D23\u540E\u7AEF\u5F00\u53D1\u300D+\u300C\u540E\u7AEF\u4F7F\u7528 Go \u548C gRPC\u300D\uFF09
326
+ \u8FD4\u56DE\uFF1A{"type": "EXPAND", "mergedContent": "\u5408\u5E76\u540E\u7684\u5B8C\u6574\u5185\u5BB9\uFF0C\u6574\u5408\u53CC\u65B9\u4FE1\u606F"}
327
+
328
+ - NEW\uFF1A\u5168\u65B0\u4FE1\u606F\uFF0C\u4E0E\u65E7\u8BB0\u5FC6\u65E0\u5B9E\u8D28\u5173\u8054\uFF08\u5982\u300C\u559C\u6B22 dark mode\u300Dvs\u300C\u4E0B\u5468\u8981\u53BB\u51FA\u5DEE\u300D\uFF09
329
+ \u8FD4\u56DE\uFF1A{"type": "NEW"}
330
+
331
+ \u53EA\u8FD4\u56DE JSON\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002`
332
+ };
333
+ templates = { en, zh };
334
+ t = templates[LOCALE];
335
+ }
336
+ });
337
+
338
+ // ../amem-core/src/llm.ts
161
339
  async function llmCall(prompt, maxTokens = 500) {
162
340
  try {
163
341
  const isThinking = MODEL.includes("gemini") || MODEL.includes("pro-agent");
@@ -381,24 +559,54 @@ ${linkedStr}`;
381
559
  return { tags: null, context: null, shouldStrengthen: false, suggestedConnections: [], tagsToUpdate: [] };
382
560
  }
383
561
  }
384
- async function qdrant(method, path52, body) {
385
- const res = await fetch(`${QDRANT_URL}${path52}`, {
386
- method,
387
- headers: { "Content-Type": "application/json" },
388
- body: body ? JSON.stringify(body) : void 0
389
- });
390
- const data = await res.json();
391
- if (!res.ok || data.status && data.status !== "ok" && data.status !== "acknowledged") {
392
- throw new Error(`Qdrant ${method} ${path52} failed: ${data.error || JSON.stringify(data)}`);
393
- }
394
- return data.result;
395
- }
396
- function resetCollectionReady() {
397
- _collectionReady = false;
398
- _collectionReadyMap.clear();
399
- }
400
- async function ensureCollection(collectionName) {
401
- const col = collectionName || getCollection();
562
+ var import_sdk, client, MODEL, VALID_CONFIDENCE, VALID_CATEGORIES, VALID_EVOLUTION_TYPES;
563
+ var init_llm = __esm({
564
+ "../amem-core/src/llm.ts"() {
565
+ "use strict";
566
+ import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
567
+ init_prompts();
568
+ client = new import_sdk.default({
569
+ ...process.env.AMEM_LLM_API_KEY && { apiKey: process.env.AMEM_LLM_API_KEY },
570
+ ...process.env.AMEM_LLM_BASE_URL && { baseURL: process.env.AMEM_LLM_BASE_URL }
571
+ });
572
+ MODEL = process.env.AMEM_LLM_MODEL ?? "claude-sonnet-4-6";
573
+ VALID_CONFIDENCE = /* @__PURE__ */ new Set(["high", "medium", "low"]);
574
+ VALID_CATEGORIES = /* @__PURE__ */ new Set([
575
+ "Technical",
576
+ "Business",
577
+ "Personal",
578
+ "Project",
579
+ "Research",
580
+ "System",
581
+ "General"
582
+ ]);
583
+ VALID_EVOLUTION_TYPES = /* @__PURE__ */ new Set(["EVOLVE", "CONFLICT", "EXPAND", "NEW"]);
584
+ }
585
+ });
586
+
587
+ // ../amem-core/src/storage.ts
588
+ async function qdrant(method, path6, body) {
589
+ const res = await fetch(`${QDRANT_URL}${path6}`, {
590
+ method,
591
+ headers: { "Content-Type": "application/json" },
592
+ body: body ? JSON.stringify(body) : void 0
593
+ });
594
+ const data = await res.json();
595
+ if (!res.ok || data.status && data.status !== "ok" && data.status !== "acknowledged") {
596
+ throw new Error(`Qdrant ${method} ${path6} failed: ${data.error || JSON.stringify(data)}`);
597
+ }
598
+ return data.result;
599
+ }
600
+ async function pingQdrant() {
601
+ const res = await fetch(`${QDRANT_URL}/readyz`);
602
+ if (!res.ok) throw new Error(`Qdrant GET /readyz failed: ${res.status}`);
603
+ }
604
+ function resetCollectionReady() {
605
+ _collectionReady = false;
606
+ _collectionReadyMap.clear();
607
+ }
608
+ async function ensureCollection(collectionName) {
609
+ const col = collectionName || getCollection();
402
610
  if (collectionName) {
403
611
  if (_collectionReadyMap.get(col)) return;
404
612
  } else {
@@ -776,6 +984,19 @@ async function patchNotePayload(id, fields) {
776
984
  async function replaceLinkReferences(oldId, newId, agentId) {
777
985
  return makeCrud(getCollection()).replaceLinkReferences(oldId, newId, agentId);
778
986
  }
987
+ var QDRANT_URL, getCollection, VECTOR_DIM, _collectionReady, _collectionReadyMap;
988
+ var init_storage = __esm({
989
+ "../amem-core/src/storage.ts"() {
990
+ "use strict";
991
+ QDRANT_URL = "http://localhost:6333";
992
+ getCollection = () => process.env.AMEM_COLLECTION || "amem_notes";
993
+ VECTOR_DIM = 384;
994
+ _collectionReady = false;
995
+ _collectionReadyMap = /* @__PURE__ */ new Map();
996
+ }
997
+ });
998
+
999
+ // ../amem-core/src/memory.ts
779
1000
  function getJieba() {
780
1001
  if (!_jieba) _jieba = new import_jieba.Jieba();
781
1002
  return _jieba;
@@ -1041,6 +1262,44 @@ async function addMemory(content, agentId = "main", opts) {
1041
1262
  console.log(`[done] Note added: ${note.id}`);
1042
1263
  return note.id;
1043
1264
  }
1265
+ async function addEpisodic(content, agentId = "main", opts) {
1266
+ const scope = opts?.scope ?? "private";
1267
+ const ctx = opts?.storageCtx ?? defaultCtx();
1268
+ const quality = checkQuality(content);
1269
+ if (!quality.ok) {
1270
+ throw new Error(`[quality] \u5199\u5165\u62D2\u7EDD: ${quality.reason}`);
1271
+ }
1272
+ const embedding = await encode(content);
1273
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1274
+ const note = {
1275
+ id: (0, import_uuid.v4)(),
1276
+ content,
1277
+ timestamp: now,
1278
+ keywords: [],
1279
+ tags: [],
1280
+ context: "",
1281
+ embedding,
1282
+ links: [],
1283
+ agent_id: scope === "shared" ? "shared" : agentId,
1284
+ hash: (0, import_crypto.createHash)("md5").update(content).digest("hex"),
1285
+ retrieval_count: 0,
1286
+ last_accessed: now,
1287
+ evolution_history: [],
1288
+ category: "General",
1289
+ is_active: true,
1290
+ note_type: "memory",
1291
+ topics: [],
1292
+ pending_merge: false,
1293
+ conflict: false,
1294
+ ephemeral: quality.ephemeral,
1295
+ low_quality: false,
1296
+ owner: agentId,
1297
+ readers: scope === "shared" ? ["*"] : [agentId],
1298
+ writers: [agentId]
1299
+ };
1300
+ await ctx.addNote(note);
1301
+ return note.id;
1302
+ }
1044
1303
  async function searchMemory(query, topK = 5, agentId = "main", opts) {
1045
1304
  const useBfs = opts?.useBfs !== false;
1046
1305
  const bfsSimThreshold = opts?.bfsSimThreshold ?? 0.25;
@@ -1130,7 +1389,7 @@ async function listMemories(agentId = "main", storageCtx) {
1130
1389
  return { count };
1131
1390
  }
1132
1391
  function sleep(ms) {
1133
- return new Promise((resolve) => setTimeout(resolve, ms));
1392
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
1134
1393
  }
1135
1394
  async function mergeSimilarNotes(agentId, storageCtx) {
1136
1395
  const ctx = storageCtx ?? defaultCtx();
@@ -1361,6 +1620,26 @@ async function consolidateMemories(agentId, logger, storageCtx) {
1361
1620
  log.info(`[Consolidation] Completed consolidation run. Merged ${mergedCount} pairs.`);
1362
1621
  return mergedCount;
1363
1622
  }
1623
+ var import_uuid, import_crypto, fs2, path3, import_jieba, _jieba, EPHEMERAL_SIGNALS;
1624
+ var init_memory = __esm({
1625
+ "../amem-core/src/memory.ts"() {
1626
+ "use strict";
1627
+ import_uuid = require("uuid");
1628
+ import_crypto = require("crypto");
1629
+ fs2 = __toESM(require("fs"), 1);
1630
+ path3 = __toESM(require("path"), 1);
1631
+ init_embedding();
1632
+ init_storage();
1633
+ init_llm();
1634
+ init_evo_counter();
1635
+ init_config();
1636
+ import_jieba = require("@node-rs/jieba");
1637
+ _jieba = null;
1638
+ EPHEMERAL_SIGNALS = ["\u5F85\u8DD1", "\u7B49\u786E\u8BA4", "\u6628\u65E5", "\u660E\u5929\u5B8C\u6210"];
1639
+ }
1640
+ });
1641
+
1642
+ // ../amem-core/src/quality.ts
1364
1643
  async function scanLowQuality(agentId) {
1365
1644
  const notes = await listNotes(agentId);
1366
1645
  const now = Date.now();
@@ -1430,10 +1709,21 @@ function severityBadge(reasons) {
1430
1709
  return "\u{1F7E0} CONFLICT";
1431
1710
  }
1432
1711
  async function generateReviewBatch(agentId, outputPath) {
1712
+ const root = path4.resolve(DEFAULT_OUTPUT_DIR);
1713
+ let filePath;
1714
+ let batchN;
1715
+ if (outputPath) {
1716
+ const name = path4.basename(outputPath);
1717
+ if (name !== outputPath || name === "" || name === "." || name === "..") {
1718
+ throw new Error(`[quality] outputPath \u5FC5\u987B\u662F\u7EAF\u6587\u4EF6\u540D\uFF08\u4E0D\u542B\u76EE\u5F55\uFF09: ${outputPath}`);
1719
+ }
1720
+ filePath = path4.join(root, name);
1721
+ batchN = 0;
1722
+ } else {
1723
+ batchN = nextBatchNumber(root);
1724
+ filePath = path4.join(root, `amem-review-batch${batchN}.md`);
1725
+ }
1433
1726
  const items = await scanLowQuality(agentId);
1434
- const dir = outputPath ? path4.dirname(outputPath) : DEFAULT_OUTPUT_DIR;
1435
- const batchN = outputPath ? 0 : nextBatchNumber(dir);
1436
- const filePath = outputPath || path4.join(dir, `amem-review-batch${batchN}.md`);
1437
1727
  const now = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1438
1728
  const lines = [];
1439
1729
  const title = LOCALE2 === "zh" ? "A-MEM \u8D28\u91CF\u5BA1\u6838" : "A-MEM Quality Review";
@@ -1483,228 +1773,84 @@ async function generateReviewBatch(agentId, outputPath) {
1483
1773
  fs3.writeFileSync(filePath, lines.join("\n"), "utf8");
1484
1774
  return filePath;
1485
1775
  }
1486
- var os, path, fs, path2, import_sdk, import_uuid, import_crypto, fs2, path3, import_jieba, fs3, path4, _dataDir, pipeline, extractor, MODEL_NAME, EVO_THRESHOLD, LOCALE, en, zh, templates, t, client, MODEL, VALID_CONFIDENCE, VALID_CATEGORIES, VALID_EVOLUTION_TYPES, QDRANT_URL, getCollection, VECTOR_DIM, _collectionReady, _collectionReadyMap, _jieba, EPHEMERAL_SIGNALS, LOCALE2, DEFAULT_OUTPUT_DIR;
1487
- var init_dist = __esm({
1488
- "../amem-core/dist/index.js"() {
1776
+ var fs3, path4, LOCALE2, DEFAULT_OUTPUT_DIR;
1777
+ var init_quality = __esm({
1778
+ "../amem-core/src/quality.ts"() {
1489
1779
  "use strict";
1490
- os = __toESM(require("os"), 1);
1491
- path = __toESM(require("path"), 1);
1492
- fs = __toESM(require("fs"), 1);
1493
- path2 = __toESM(require("path"), 1);
1494
- import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
1495
- import_uuid = require("uuid");
1496
- import_crypto = require("crypto");
1497
- fs2 = __toESM(require("fs"), 1);
1498
- path3 = __toESM(require("path"), 1);
1499
- import_jieba = require("@node-rs/jieba");
1500
1780
  fs3 = __toESM(require("fs"), 1);
1501
1781
  path4 = __toESM(require("path"), 1);
1502
- _dataDir = process.env.AMEM_DATA_DIR || path.join(os.homedir(), ".amem");
1503
- pipeline = null;
1504
- extractor = null;
1505
- MODEL_NAME = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
1506
- EVO_THRESHOLD = 20;
1507
- LOCALE = process.env.AMEM_PROMPT_LOCALE === "zh" ? "zh" : "en";
1508
- en = {
1509
- crudDecision: (userText, assistantText, memoryList) => `You are a memory management agent. Analyze the conversation and decide what memory operations are needed.
1510
-
1511
- ## Conversation
1512
-
1513
- User: ${userText}
1514
- Assistant: ${assistantText}
1515
-
1516
- ## Existing relevant memories (identified by integer idx)
1517
-
1518
- ${memoryList}
1519
-
1520
- ## Task
1521
-
1522
- Extract only genuinely important long-term facts (decisions, preferences, account info, project status, key insights). Skip small talk, confirmations, and information already captured in existing memories.
1523
-
1524
- ## Operation types
1525
- - NEW: Extract a brand new fact not present in existing memories
1526
- - UPDATE: New information refines or supersedes an existing memory; specify existingIdx
1527
- - DELETE: An existing memory is outdated, contradicted, or wrong; specify existingIdx, fact = original content
1528
- - NONE: Nothing worth recording, or information already fully captured
1529
-
1530
- ## Output format
1531
-
1532
- Return a JSON array. Each item:
1533
- {"action": "NEW"|"UPDATE"|"DELETE"|"NONE", "fact": "fact content", "existingIdx": integer or omit, "reason": "optional"}
1534
-
1535
- Return at most 3 operations. If nothing is worth recording, return [].
1536
- Return only the JSON array, no other text.
1537
-
1538
- Examples:
1539
-
1540
- 1. New preference:
1541
- [{"action": "NEW", "fact": "User prefers TypeScript over JavaScript", "reason": "Explicitly stated tech preference"}]
1542
-
1543
- 2. Updating an existing memory (idx 0 was "User is evaluating React and Vue"):
1544
- [{"action": "UPDATE", "fact": "User decided to use React (dropped Vue)", "existingIdx": 0, "reason": "Decision finalized, update evaluation status"}]
1545
-
1546
- 3. Conversation is just "Sure, thanks" / "Got it" with no new info:
1547
- []`,
1548
- shouldMerge: (contentA, contentB) => `You are a memory deduplication assistant. Determine whether two memories express essentially the same information.
1549
-
1550
- Memory A: ${contentA}
1551
- Memory B: ${contentB}
1552
-
1553
- Rules:
1554
- - If both memories express the same core fact (possibly different wording or granularity), return:
1555
- {"shouldMerge": true, "merged": "Concise merged statement preserving key details from both, more complete than either alone"}
1556
- - If the memories are complementary, on different topics, or contain different specific facts, return:
1557
- {"shouldMerge": false}
1558
-
1559
- Return only JSON, no other text.
1560
-
1561
- Examples:
1562
-
1563
- 1. Should merge (different granularity):
1564
- A: "Project uses PostgreSQL"
1565
- B: "Project's primary database is PostgreSQL 16, deployed on AWS RDS"
1566
- -> {"shouldMerge": true, "merged": "Project uses PostgreSQL 16 as primary database, deployed on AWS RDS"}
1567
-
1568
- 2. Should NOT merge (complementary but distinct):
1569
- A: "User prefers VS Code"
1570
- B: "User's VS Code uses One Dark Pro theme"
1571
- -> {"shouldMerge": false}`,
1572
- evolutionJudge: (oldContent, newContent) => `You are a memory evolution judge. Analyze the relationship between an old and new memory and return JSON.
1573
-
1574
- Old memory: ${oldContent}
1575
- New memory: ${newContent}
1576
-
1577
- Classification rules:
1578
-
1579
- - EVOLVE: New content deepens or updates the old memory (e.g. "Considering Next.js" -> "Decided on Next.js 14 App Router")
1580
- Return: {"type": "EVOLVE", "mergedContent": "Merged content preserving the evolution trajectory"}
1581
-
1582
- - CONFLICT: Old and new information directly contradict each other on the same attribute (e.g. "Uses MySQL as primary DB" vs "Migrated to PostgreSQL")
1583
- Return: {"type": "CONFLICT"}
1584
-
1585
- - EXPAND: New information supplements the old memory on the same topic (e.g. "Handles backend dev" + "Backend uses Go and gRPC")
1586
- Return: {"type": "EXPAND", "mergedContent": "Merged content integrating both pieces of information"}
1587
-
1588
- - NEW: Completely unrelated information, no substantive connection to the old memory
1589
- Return: {"type": "NEW"}
1590
-
1591
- Return only JSON, no other text.`
1592
- };
1593
- zh = {
1594
- crudDecision: (userText, assistantText, memoryList) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u7BA1\u7406 agent\uFF0C\u8D1F\u8D23\u5206\u6790\u5BF9\u8BDD\u5185\u5BB9\u5E76\u51B3\u5B9A\u5982\u4F55\u64CD\u4F5C\u8BB0\u5FC6\u5E93\u3002
1595
-
1596
- ## \u5BF9\u8BDD\u5185\u5BB9
1597
-
1598
- \u7528\u6237\uFF1A${userText}
1599
- \u52A9\u624B\uFF1A${assistantText}
1600
-
1601
- ## \u5DF2\u6709\u76F8\u5173\u8BB0\u5FC6\uFF08\u7528\u6574\u6570 idx \u6807\u8BC6\uFF09
1602
-
1603
- ${memoryList}
1604
-
1605
- ## \u4EFB\u52A1
1606
-
1607
- \u5206\u6790\u4E0A\u8FF0\u5BF9\u8BDD\uFF0C\u51B3\u5B9A\u9700\u8981\u54EA\u4E9B\u8BB0\u5FC6\u64CD\u4F5C\u3002\u53EA\u63D0\u53D6\u771F\u6B63\u91CD\u8981\u7684\u957F\u671F\u4E8B\u5B9E\uFF08\u51B3\u7B56\u3001\u504F\u597D\u3001\u8D26\u53F7\u4FE1\u606F\u3001\u9879\u76EE\u72B6\u6001\u3001\u5173\u952E\u6D1E\u5BDF\uFF09\u3002\u8DF3\u8FC7\u95F2\u804A\u3001\u786E\u8BA4\u8BED\u3001\u91CD\u590D\u4FE1\u606F\u3002
1608
-
1609
- ## \u64CD\u4F5C\u7C7B\u578B
1610
- - NEW\uFF1A\u63D0\u53D6\u5168\u65B0\u4E8B\u5B9E\uFF08\u5DF2\u6709\u8BB0\u5FC6\u4E2D\u6CA1\u6709\u7684\u4FE1\u606F\uFF09
1611
- - UPDATE\uFF1A\u65B0\u4FE1\u606F\u66F4\u65B0\u4E86\u67D0\u6761\u5DF2\u6709\u8BB0\u5FC6\uFF0C\u7528 existingIdx \u6307\u5B9A\u8981\u66F4\u65B0\u7684\u6761\u76EE
1612
- - DELETE\uFF1A\u67D0\u6761\u5DF2\u6709\u8BB0\u5FC6\u5DF2\u7ECF\u8FC7\u65F6\u3001\u53D1\u751F\u51B2\u7A81\u6216\u9519\u8BEF\uFF0C\u7528 existingIdx \u6307\u5B9A\uFF0Cfact \u586B\u539F\u5185\u5BB9
1613
- - NONE\uFF1A\u4E0D\u503C\u5F97\u8BB0\u5F55\u6216\u5DF2\u6709\u5B8C\u5168\u76F8\u540C\u7684\u4FE1\u606F
1614
-
1615
- ## \u8F93\u51FA\u683C\u5F0F
1616
-
1617
- \u8FD4\u56DE JSON \u6570\u7EC4\uFF0C\u6BCF\u6761\u683C\u5F0F\uFF1A
1618
- {"action": "NEW"|"UPDATE"|"DELETE"|"NONE", "fact": "\u4E8B\u5B9E\u5185\u5BB9", "existingIdx": \u6574\u6570\u6216\u7701\u7565, "reason": "\u539F\u56E0\uFF08\u53EF\u9009\uFF09"}
1619
-
1620
- \u6BCF\u6B21\u6700\u591A\u8FD4\u56DE 3 \u6761\u64CD\u4F5C\u3002\u5982\u679C\u6CA1\u6709\u503C\u5F97\u64CD\u4F5C\u7684\u5185\u5BB9\uFF0C\u8FD4\u56DE []\u3002
1621
- \u53EA\u8FD4\u56DE JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002
1622
-
1623
- \u793A\u4F8B\uFF1A
1624
-
1625
- 1. \u63D0\u53D6\u65B0\u504F\u597D\uFF1A
1626
- [{"action": "NEW", "fact": "\u7528\u6237\u504F\u597D TypeScript \u800C\u975E JavaScript", "reason": "\u660E\u786E\u8868\u8FBE\u7684\u6280\u672F\u504F\u597D"}]
1627
-
1628
- 2. \u66F4\u65B0\u5DF2\u6709\u8BB0\u5FC6\uFF08idx 0 \u539F\u4E3A"\u7528\u6237\u6B63\u5728\u8BC4\u4F30 React \u548C Vue"\uFF09\uFF1A
1629
- [{"action": "UPDATE", "fact": "\u7528\u6237\u51B3\u5B9A\u4F7F\u7528 React\uFF08\u653E\u5F03\u4E86 Vue\uFF09", "existingIdx": 0, "reason": "\u51B3\u7B56\u5DF2\u660E\u786E\uFF0C\u66F4\u65B0\u8BC4\u4F30\u72B6\u6001"}]
1630
-
1631
- 3. \u5BF9\u8BDD\u4EC5\u4E3A"\u597D\u7684\uFF0C\u8C22\u8C22"/"\u6CA1\u95EE\u9898"\u7B49\u786E\u8BA4\u8BED\uFF0C\u65E0\u65B0\u4FE1\u606F\uFF1A
1632
- []`,
1633
- shouldMerge: (contentA, contentB) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u53BB\u91CD\u52A9\u624B\uFF0C\u8D1F\u8D23\u5224\u65AD\u4E24\u6761\u8BB0\u5FC6\u662F\u5426\u8868\u8FBE\u4E86\u672C\u8D28\u76F8\u540C\u7684\u4FE1\u606F\u3002
1634
-
1635
- \u8BB0\u5FC6A\uFF1A${contentA}
1636
- \u8BB0\u5FC6B\uFF1A${contentB}
1637
-
1638
- \u5224\u65AD\u89C4\u5219\uFF1A
1639
- - \u5982\u679C\u4E24\u6761\u8BB0\u5FC6\u8868\u8FBE\u7684\u662F\u672C\u8D28\u76F8\u540C\u7684\u4FE1\u606F\uFF08\u53EF\u80FD\u63AA\u8F9E\u4E0D\u540C\u3001\u7C92\u5EA6\u4E0D\u540C\uFF0C\u4F46\u6838\u5FC3\u4E8B\u5B9E\u4E00\u81F4\uFF09\uFF0C\u8FD4\u56DE JSON\uFF1A
1640
- {"shouldMerge": true, "merged": "\u5408\u5E76\u540E\u7684\u7B80\u6D01\u8868\u8FF0\uFF0C\u4FDD\u7559\u4E24\u6761\u8BB0\u5FC6\u7684\u5173\u952E\u4FE1\u606F\uFF0C\u6BD4\u4EFB\u4F55\u4E00\u6761\u90FD\u66F4\u5B8C\u6574"}
1641
- - \u5982\u679C\u4E24\u6761\u8BB0\u5FC6\u662F\u4E92\u8865\u4FE1\u606F\u3001\u4E0D\u540C\u4E3B\u9898\u3001\u6216\u5305\u542B\u4E0D\u540C\u7684\u5177\u4F53\u4E8B\u5B9E\uFF0C\u8FD4\u56DE JSON\uFF1A
1642
- {"shouldMerge": false}
1643
-
1644
- \u53EA\u8FD4\u56DE JSON\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002
1645
-
1646
- \u793A\u4F8B\uFF1A
1647
-
1648
- 1. \u5E94\u5408\u5E76\uFF08\u7C92\u5EA6\u4E0D\u540C\uFF09\uFF1A
1649
- A: "\u9879\u76EE\u4F7F\u7528 PostgreSQL \u6570\u636E\u5E93"
1650
- B: "\u9879\u76EE\u7684\u4E3B\u6570\u636E\u5E93\u662F PostgreSQL 16\uFF0C\u90E8\u7F72\u5728 AWS RDS \u4E0A"
1651
- \u2192 {"shouldMerge": true, "merged": "\u9879\u76EE\u4F7F\u7528 PostgreSQL 16 \u4F5C\u4E3A\u4E3B\u6570\u636E\u5E93\uFF0C\u90E8\u7F72\u5728 AWS RDS \u4E0A"}
1652
-
1653
- 2. \u4E0D\u5E94\u5408\u5E76\uFF08\u4E92\u8865\u4F46\u4E0D\u540C\uFF09\uFF1A
1654
- A: "\u7528\u6237\u559C\u6B22\u7528 VS Code"
1655
- B: "\u7528\u6237\u7684 VS Code \u4F7F\u7528 One Dark Pro \u4E3B\u9898"
1656
- \u2192 {"shouldMerge": false}`,
1657
- evolutionJudge: (oldContent, newContent) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u6F14\u5316\u5224\u65AD\u52A9\u624B\u3002\u5206\u6790\u4EE5\u4E0B\u4E24\u6761\u8BB0\u5FC6\u7684\u5173\u7CFB\u5E76\u8FD4\u56DE JSON\u3002
1658
-
1659
- \u65E7\u8BB0\u5FC6\uFF1A${oldContent}
1660
- \u65B0\u8BB0\u5FC6\uFF1A${newContent}
1661
-
1662
- \u5224\u65AD\u89C4\u5219\uFF1A
1663
-
1664
- - EVOLVE\uFF1A\u65B0\u5185\u5BB9\u662F\u5BF9\u65E7\u8BB0\u5FC6\u7684\u6DF1\u5316/\u66F4\u65B0\uFF08\u5982\u300C\u6B63\u5728\u8003\u8651\u7528 Next.js\u300D\u2192\u300C\u51B3\u5B9A\u7528 Next.js 14 App Router\u300D\uFF09
1665
- \u8FD4\u56DE\uFF1A{"type": "EVOLVE", "mergedContent": "\u878D\u5408\u540E\u7684\u5B8C\u6574\u5185\u5BB9\uFF0C\u4FDD\u7559\u6F14\u5316\u8F68\u8FF9"}
1666
-
1667
- - CONFLICT\uFF1A\u65B0\u65E7\u4FE1\u606F\u5728\u540C\u4E00\u5C5E\u6027\u4E0A\u76F4\u63A5\u77DB\u76FE\uFF08\u5982\u300C\u4F7F\u7528 MySQL \u4F5C\u4E3A\u4E3B\u6570\u636E\u5E93\u300Dvs\u300C\u5DF2\u8FC1\u79FB\u5230 PostgreSQL\u300D\uFF09
1668
- \u8FD4\u56DE\uFF1A{"type": "CONFLICT"}
1669
-
1670
- - EXPAND\uFF1A\u65B0\u4FE1\u606F\u662F\u5BF9\u65E7\u8BB0\u5FC6\u540C\u4E00\u4E3B\u9898\u7684\u8865\u5145\u6269\u5C55\uFF08\u5982\u300C\u8D1F\u8D23\u540E\u7AEF\u5F00\u53D1\u300D+\u300C\u540E\u7AEF\u4F7F\u7528 Go \u548C gRPC\u300D\uFF09
1671
- \u8FD4\u56DE\uFF1A{"type": "EXPAND", "mergedContent": "\u5408\u5E76\u540E\u7684\u5B8C\u6574\u5185\u5BB9\uFF0C\u6574\u5408\u53CC\u65B9\u4FE1\u606F"}
1672
-
1673
- - NEW\uFF1A\u5168\u65B0\u4FE1\u606F\uFF0C\u4E0E\u65E7\u8BB0\u5FC6\u65E0\u5B9E\u8D28\u5173\u8054\uFF08\u5982\u300C\u559C\u6B22 dark mode\u300Dvs\u300C\u4E0B\u5468\u8981\u53BB\u51FA\u5DEE\u300D\uFF09
1674
- \u8FD4\u56DE\uFF1A{"type": "NEW"}
1675
-
1676
- \u53EA\u8FD4\u56DE JSON\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002`
1677
- };
1678
- templates = { en, zh };
1679
- t = templates[LOCALE];
1680
- client = new import_sdk.default({
1681
- ...process.env.AMEM_LLM_API_KEY && { apiKey: process.env.AMEM_LLM_API_KEY },
1682
- ...process.env.AMEM_LLM_BASE_URL && { baseURL: process.env.AMEM_LLM_BASE_URL }
1683
- });
1684
- MODEL = process.env.AMEM_LLM_MODEL ?? "claude-sonnet-4-6";
1685
- VALID_CONFIDENCE = /* @__PURE__ */ new Set(["high", "medium", "low"]);
1686
- VALID_CATEGORIES = /* @__PURE__ */ new Set([
1687
- "Technical",
1688
- "Business",
1689
- "Personal",
1690
- "Project",
1691
- "Research",
1692
- "System",
1693
- "General"
1694
- ]);
1695
- VALID_EVOLUTION_TYPES = /* @__PURE__ */ new Set(["EVOLVE", "CONFLICT", "EXPAND", "NEW"]);
1696
- QDRANT_URL = "http://localhost:6333";
1697
- getCollection = () => process.env.AMEM_COLLECTION || "amem_notes";
1698
- VECTOR_DIM = 384;
1699
- _collectionReady = false;
1700
- _collectionReadyMap = /* @__PURE__ */ new Map();
1701
- _jieba = null;
1702
- EPHEMERAL_SIGNALS = ["\u5F85\u8DD1", "\u7B49\u786E\u8BA4", "\u6628\u65E5", "\u660E\u5929\u5B8C\u6210"];
1782
+ init_storage();
1703
1783
  LOCALE2 = process.env.AMEM_PROMPT_LOCALE === "zh" ? "zh" : "en";
1704
1784
  DEFAULT_OUTPUT_DIR = process.env.AMEM_REVIEW_DIR || process.cwd();
1705
1785
  }
1706
1786
  });
1707
1787
 
1788
+ // ../amem-core/src/index.ts
1789
+ var src_exports = {};
1790
+ __export(src_exports, {
1791
+ addEpisodic: () => addEpisodic,
1792
+ addMemory: () => addMemory,
1793
+ addNote: () => addNote,
1794
+ bm25Score: () => bm25Score,
1795
+ buildBM25: () => buildBM25,
1796
+ checkQuality: () => checkQuality,
1797
+ configure: () => configure,
1798
+ consolidateMemories: () => consolidateMemories,
1799
+ cosineSimilarity: () => cosineSimilarity,
1800
+ countNotes: () => countNotes,
1801
+ createStorageContext: () => createStorageContext,
1802
+ deleteNote: () => deleteNote,
1803
+ encode: () => encode,
1804
+ ensureCollection: () => ensureCollection,
1805
+ findByHash: () => findByHash,
1806
+ generateReviewBatch: () => generateReviewBatch,
1807
+ getDataDir: () => getDataDir,
1808
+ getEvoCount: () => getEvoCount,
1809
+ getNote: () => getNote,
1810
+ getNotesByDatePrefix: () => getNotesByDatePrefix,
1811
+ incrementEvoCount: () => incrementEvoCount,
1812
+ invalidateNote: () => invalidateNote,
1813
+ isModelLoaded: () => isModelLoaded,
1814
+ listMemories: () => listMemories,
1815
+ listNotes: () => listNotes,
1816
+ llmCall: () => llmCall,
1817
+ llmConstructNote: () => llmConstructNote,
1818
+ llmCrudDecision: () => llmCrudDecision,
1819
+ llmEvolutionJudge: () => llmEvolutionJudge,
1820
+ llmEvolveNote: () => llmEvolveNote,
1821
+ llmShouldLink: () => llmShouldLink,
1822
+ llmShouldMerge: () => llmShouldMerge,
1823
+ loadModel: () => loadModel,
1824
+ mergeSimilarNotes: () => mergeSimilarNotes,
1825
+ patchNotePayload: () => patchNotePayload,
1826
+ pingQdrant: () => pingQdrant,
1827
+ queryByEmbedding: () => queryByEmbedding,
1828
+ replaceLinkReferences: () => replaceLinkReferences,
1829
+ resetCollectionReady: () => resetCollectionReady,
1830
+ rrfMerge: () => rrfMerge,
1831
+ scanLowQuality: () => scanLowQuality,
1832
+ searchMemory: () => searchMemory,
1833
+ shouldRunEvolution: () => shouldRunEvolution,
1834
+ simpleTokenize: () => simpleTokenize,
1835
+ t: () => t,
1836
+ updateNote: () => updateNote,
1837
+ updateNoteContent: () => updateNoteContent,
1838
+ updateNoteLinks: () => updateNoteLinks
1839
+ });
1840
+ var init_src = __esm({
1841
+ "../amem-core/src/index.ts"() {
1842
+ "use strict";
1843
+ init_config();
1844
+ init_embedding();
1845
+ init_evo_counter();
1846
+ init_llm();
1847
+ init_memory();
1848
+ init_prompts();
1849
+ init_quality();
1850
+ init_storage();
1851
+ }
1852
+ });
1853
+
1708
1854
  // src/index.ts
1709
1855
  var index_exports = {};
1710
1856
  __export(index_exports, {
@@ -1733,7 +1879,7 @@ module.exports = __toCommonJS(index_exports);
1733
1879
  var os2 = __toESM(require("os"));
1734
1880
  var path5 = __toESM(require("path"));
1735
1881
  var import_plugin_entry = require("openclaw/plugin-sdk/plugin-entry");
1736
- init_dist();
1882
+ init_src();
1737
1883
  var import_crypto2 = require("crypto");
1738
1884
 
1739
1885
  // src/hook-liveness.ts
@@ -1762,7 +1908,7 @@ function hookNeverFiredWarning(now = Date.now()) {
1762
1908
  }
1763
1909
 
1764
1910
  // src/index.ts
1765
- init_dist();
1911
+ init_src();
1766
1912
  var _config = {};
1767
1913
  function register(api) {
1768
1914
  const logger = api.logger;
@@ -2040,7 +2186,7 @@ ${text}${hookWarning}` }],
2040
2186
  properties: {
2041
2187
  outputPath: {
2042
2188
  type: "string",
2043
- description: "Custom output path for the review batch file (optional, auto-generates if omitted)"
2189
+ description: "Custom filename for the review batch (optional, auto-generates if omitted). A bare filename only \u2014 it is written under AMEM_REVIEW_DIR; a path with directories is rejected."
2044
2190
  }
2045
2191
  },
2046
2192
  required: []
@@ -2110,7 +2256,7 @@ ${text}${hookWarning}` }],
2110
2256
  id: r.id,
2111
2257
  content: r.content
2112
2258
  }));
2113
- const { llmCrudDecision: llmCrudDecision2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
2259
+ const { llmCrudDecision: llmCrudDecision2 } = await Promise.resolve().then(() => (init_src(), src_exports));
2114
2260
  const operations = await llmCrudDecision2(
2115
2261
  userText,
2116
2262
  assistantText,
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "id": "openclaw-amem",
3
3
  "name": "Memory (A-MEM v2)",
4
- "description": "A-MEM agentic memory backend for OpenClawQdrant + Transformers.js, no Python required. Note construction, link generation, memory evolution.",
5
- "version": "1.1.3",
4
+ "description": "OpenClaw memory plugin implementing A-MEMmemories evolve, not just accumulate. Graph linking, hybrid retrieval, LLM-driven evolution. No Python.",
5
+ "version": "1.1.5",
6
6
  "kind": "memory",
7
7
  "openclaw": {
8
8
  "compat": {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "openclaw-amem",
3
- "version": "1.1.3",
4
- "description": "A-MEM agentic memory backend for OpenClaw TypeScript rewrite",
3
+ "version": "1.1.5",
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",
7
7
  "openclaw": {
@@ -16,23 +16,23 @@
16
16
  }
17
17
  },
18
18
  "dependencies": {
19
- "@anthropic-ai/sdk": "^0.52.0",
20
- "@huggingface/transformers": "^3.5.0",
19
+ "@anthropic-ai/sdk": "^0.110.0",
20
+ "@huggingface/transformers": "^4.2.0",
21
21
  "@node-rs/jieba": "^2.0.1",
22
22
  "@qdrant/js-client-rest": "^1.18.0",
23
- "@types/uuid": "^10.0.0",
23
+ "@types/uuid": "^11.0.0",
24
24
  "uuid": "^14.0.0"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@eslint/js": "^10.0.1",
28
+ "@types/node": "^26.1.1",
28
29
  "eslint": "^10.4.1",
29
30
  "prettier": "^3.8.3",
30
31
  "tsup": "^8.4.0",
31
32
  "tsx": "^4.21.0",
32
33
  "typescript": "^5.9.3",
33
34
  "typescript-eslint": "^8.60.1",
34
- "vitest": "^4.1.8",
35
- "amem-core": "0.1.0"
35
+ "vitest": "^4.1.10"
36
36
  },
37
37
  "optionalDependencies": {
38
38
  "@node-rs/jieba-darwin-arm64": "^2.0.1",
@@ -68,6 +68,7 @@
68
68
  "scripts": {
69
69
  "build": "tsup src/index.ts --format cjs --out-dir dist",
70
70
  "dev": "tsup src/index.ts --format cjs --watch",
71
+ "typecheck": "tsc --noEmit",
71
72
  "lint": "eslint src",
72
73
  "format": "prettier --check \"src/**/*.ts\"",
73
74
  "format:fix": "prettier --write \"src/**/*.ts\"",