openclaw-cortex-memory 0.1.0-Alpha.1 → 0.1.0-Alpha.11

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.
Files changed (65) hide show
  1. package/README.md +118 -28
  2. package/SKILL.md +112 -31
  3. package/dist/index.d.ts +48 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +359 -19
  6. package/dist/index.js.map +1 -1
  7. package/dist/openclaw.plugin.json +157 -5
  8. package/dist/src/dedup/three_stage_deduplicator.d.ts +25 -0
  9. package/dist/src/dedup/three_stage_deduplicator.d.ts.map +1 -0
  10. package/dist/src/dedup/three_stage_deduplicator.js +225 -0
  11. package/dist/src/dedup/three_stage_deduplicator.js.map +1 -0
  12. package/dist/src/engine/memory_engine.d.ts +2 -1
  13. package/dist/src/engine/memory_engine.d.ts.map +1 -1
  14. package/dist/src/engine/ts_engine.d.ts +95 -0
  15. package/dist/src/engine/ts_engine.d.ts.map +1 -1
  16. package/dist/src/engine/ts_engine.js +869 -33
  17. package/dist/src/engine/ts_engine.js.map +1 -1
  18. package/dist/src/engine/types.d.ts +11 -0
  19. package/dist/src/engine/types.d.ts.map +1 -1
  20. package/dist/src/graph/ontology.d.ts +53 -0
  21. package/dist/src/graph/ontology.d.ts.map +1 -0
  22. package/dist/src/graph/ontology.js +252 -0
  23. package/dist/src/graph/ontology.js.map +1 -0
  24. package/dist/src/reflect/reflector.d.ts +7 -0
  25. package/dist/src/reflect/reflector.d.ts.map +1 -1
  26. package/dist/src/reflect/reflector.js +75 -1
  27. package/dist/src/reflect/reflector.js.map +1 -1
  28. package/dist/src/session/session_end.d.ts +56 -0
  29. package/dist/src/session/session_end.d.ts.map +1 -1
  30. package/dist/src/session/session_end.js +270 -55
  31. package/dist/src/session/session_end.js.map +1 -1
  32. package/dist/src/store/archive_store.d.ts +115 -0
  33. package/dist/src/store/archive_store.d.ts.map +1 -0
  34. package/dist/src/store/archive_store.js +446 -0
  35. package/dist/src/store/archive_store.js.map +1 -0
  36. package/dist/src/store/embedding_utils.d.ts +32 -0
  37. package/dist/src/store/embedding_utils.d.ts.map +1 -0
  38. package/dist/src/store/embedding_utils.js +173 -0
  39. package/dist/src/store/embedding_utils.js.map +1 -0
  40. package/dist/src/store/read_store.d.ts +59 -0
  41. package/dist/src/store/read_store.d.ts.map +1 -1
  42. package/dist/src/store/read_store.js +1114 -17
  43. package/dist/src/store/read_store.js.map +1 -1
  44. package/dist/src/store/vector_store.d.ts +43 -0
  45. package/dist/src/store/vector_store.d.ts.map +1 -0
  46. package/dist/src/store/vector_store.js +186 -0
  47. package/dist/src/store/vector_store.js.map +1 -0
  48. package/dist/src/store/write_store.d.ts +45 -0
  49. package/dist/src/store/write_store.d.ts.map +1 -1
  50. package/dist/src/store/write_store.js +230 -0
  51. package/dist/src/store/write_store.js.map +1 -1
  52. package/dist/src/sync/session_sync.d.ts +52 -2
  53. package/dist/src/sync/session_sync.d.ts.map +1 -1
  54. package/dist/src/sync/session_sync.js +474 -22
  55. package/dist/src/sync/session_sync.js.map +1 -1
  56. package/dist/src/utils/runtime_env.d.ts +4 -0
  57. package/dist/src/utils/runtime_env.d.ts.map +1 -0
  58. package/dist/src/utils/runtime_env.js +51 -0
  59. package/dist/src/utils/runtime_env.js.map +1 -0
  60. package/openclaw.plugin.json +157 -5
  61. package/package.json +21 -6
  62. package/scripts/cli.js +19 -14
  63. package/scripts/uninstall.js +22 -5
  64. package/index.ts +0 -2071
  65. package/scripts/install.js +0 -27
package/dist/index.js CHANGED
@@ -46,10 +46,14 @@ const net = __importStar(require("net"));
46
46
  const ts_engine_1 = require("./src/engine/ts_engine");
47
47
  const read_store_1 = require("./src/store/read_store");
48
48
  const write_store_1 = require("./src/store/write_store");
49
+ const archive_store_1 = require("./src/store/archive_store");
50
+ const vector_store_1 = require("./src/store/vector_store");
49
51
  const session_sync_1 = require("./src/sync/session_sync");
50
52
  const session_end_1 = require("./src/session/session_end");
51
53
  const rule_store_1 = require("./src/rules/rule_store");
52
54
  const reflector_1 = require("./src/reflect/reflector");
55
+ const three_stage_deduplicator_1 = require("./src/dedup/three_stage_deduplicator");
56
+ const runtime_env_1 = require("./src/utils/runtime_env");
53
57
  const ERROR_CODES = {
54
58
  CONNECTION_REFUSED: {
55
59
  code: "E001",
@@ -88,7 +92,66 @@ const MIN_OPENCLAW_VERSION = "2026.3.8";
88
92
  const MAX_OPENCLAW_VERSION = "2027.0.0";
89
93
  const defaultConfig = {
90
94
  autoSync: true,
95
+ llmRequiredForWrite: true,
91
96
  autoReflect: false,
97
+ autoReflectIntervalMinutes: 30,
98
+ readFusion: {
99
+ enabled: true,
100
+ maxCandidates: 10,
101
+ authoritative: true,
102
+ channelWeights: {
103
+ rules: 1,
104
+ archive: 1.15,
105
+ vector: 1.2,
106
+ graph: 1,
107
+ },
108
+ channelTopK: {
109
+ rules: 8,
110
+ archive: 20,
111
+ vector: 20,
112
+ graph: 12,
113
+ },
114
+ minLexicalHits: 1,
115
+ minSemanticHits: 1,
116
+ lengthNorm: {
117
+ enabled: true,
118
+ pivotChars: 1200,
119
+ strength: 0.75,
120
+ minFactor: 0.45,
121
+ },
122
+ },
123
+ vectorChunking: {
124
+ chunkSize: 600,
125
+ chunkOverlap: 100,
126
+ },
127
+ memoryDecay: {
128
+ enabled: true,
129
+ minFloor: 0.15,
130
+ defaultHalfLifeDays: 90,
131
+ antiDecay: {
132
+ enabled: true,
133
+ maxBoost: 1.6,
134
+ hitWeight: 0.08,
135
+ recentWindowDays: 30,
136
+ },
137
+ halfLifeByEventType: {
138
+ issue: 30,
139
+ fix: 30,
140
+ action_item: 30,
141
+ blocker: 30,
142
+ plan: 60,
143
+ milestone: 60,
144
+ follow_up: 60,
145
+ decision: 120,
146
+ insight: 120,
147
+ retrospective: 120,
148
+ preference: 240,
149
+ constraint: 240,
150
+ requirement: 240,
151
+ dependency: 240,
152
+ assumption: 240,
153
+ },
154
+ },
92
155
  enabled: true,
93
156
  fallbackToBuiltin: true,
94
157
  engineMode: "ts",
@@ -96,6 +159,7 @@ const defaultConfig = {
96
159
  let autoSearchCacheBySession = new Map();
97
160
  const AUTO_SEARCH_CACHE_TTL = 60000;
98
161
  const MAX_AUTO_SEARCH_CACHE_SESSIONS = 200;
162
+ const HOOK_GUARD_TIMEOUT_MS = 2000;
99
163
  let config = null;
100
164
  let logger;
101
165
  let pythonProcess = null;
@@ -150,6 +214,33 @@ function getSessionCachedAutoSearch(sessionId) {
150
214
  ageSeconds: Math.floor((Date.now() - cache.timestamp) / 1000),
151
215
  };
152
216
  }
217
+ function isInternalSession(sessionId) {
218
+ if (!sessionId)
219
+ return false;
220
+ return sessionId.startsWith("slug-generator-") || sessionId.startsWith("fallback:");
221
+ }
222
+ async function runWithTimeout(task, timeoutMs, label) {
223
+ let timeoutHandle = null;
224
+ try {
225
+ const timeoutPromise = new Promise((resolve) => {
226
+ timeoutHandle = setTimeout(() => resolve(null), timeoutMs);
227
+ });
228
+ const result = await Promise.race([task, timeoutPromise]);
229
+ if (result === null) {
230
+ logger.warn(`${label} timed out after ${timeoutMs}ms; skipped to protect gateway responsiveness`);
231
+ }
232
+ return result;
233
+ }
234
+ catch (error) {
235
+ logger.warn(`${label} failed: ${error instanceof Error ? error.message : String(error)}`);
236
+ return null;
237
+ }
238
+ finally {
239
+ if (timeoutHandle) {
240
+ clearTimeout(timeoutHandle);
241
+ }
242
+ }
243
+ }
153
244
  function resolveEngine() {
154
245
  if (!config) {
155
246
  throw new Error("Configuration not loaded");
@@ -164,16 +255,44 @@ function resolveEngine() {
164
255
  projectRoot,
165
256
  dbPath: config.dbPath,
166
257
  logger,
258
+ embedding: config.embedding,
259
+ reranker: config.reranker,
260
+ llm: config.llm,
261
+ fusion: config.readFusion,
262
+ memoryDecay: config.memoryDecay,
263
+ });
264
+ const vectorStore = (0, vector_store_1.createVectorStore)({
265
+ memoryRoot,
266
+ logger,
167
267
  });
168
268
  const writeStore = (0, write_store_1.createWriteStore)({
169
269
  projectRoot,
170
270
  dbPath: config.dbPath,
171
271
  logger,
272
+ embedding: config.embedding,
273
+ vectorChunking: config.vectorChunking,
274
+ vectorStore,
275
+ });
276
+ const deduplicator = (0, three_stage_deduplicator_1.createThreeStageDeduplicator)({
277
+ memoryRoot,
278
+ logger,
279
+ });
280
+ const archiveStore = (0, archive_store_1.createArchiveStore)({
281
+ projectRoot,
282
+ memoryRoot,
283
+ logger,
284
+ embedding: config.embedding,
285
+ vectorChunking: config.vectorChunking,
286
+ deduplicator,
287
+ vectorStore,
172
288
  });
173
289
  const sessionSync = (0, session_sync_1.createSessionSync)({
174
290
  projectRoot,
175
291
  dbPath: config.dbPath,
176
292
  logger,
293
+ llm: config.llm,
294
+ requireLlmForWrite: config.llmRequiredForWrite ?? true,
295
+ archiveStore,
177
296
  writeStore,
178
297
  });
179
298
  const sessionEnd = (0, session_end_1.createSessionEnd)({
@@ -181,6 +300,10 @@ function resolveEngine() {
181
300
  dbPath: config.dbPath,
182
301
  logger,
183
302
  syncMemory: sessionSync.syncMemory,
303
+ syncDailySummaries: sessionSync.syncDailySummaries,
304
+ archiveStore,
305
+ llm: config.llm,
306
+ requireLlmForWrite: config.llmRequiredForWrite ?? true,
184
307
  });
185
308
  const ruleStore = (0, rule_store_1.createRuleStore)({
186
309
  projectRoot,
@@ -192,14 +315,22 @@ function resolveEngine() {
192
315
  dbPath: config.dbPath,
193
316
  logger,
194
317
  ruleStore,
318
+ llm: config.llm,
195
319
  });
196
320
  memoryEngine = (0, ts_engine_1.createTsEngine)({
197
321
  readStore,
198
322
  writeStore,
323
+ vectorStore,
324
+ archiveStore,
199
325
  sessionSync,
200
326
  sessionEnd,
201
327
  reflector,
202
328
  memoryRoot,
329
+ projectRoot,
330
+ embedding: config.embedding,
331
+ llm: config.llm,
332
+ reranker: config.reranker,
333
+ vectorChunking: config.vectorChunking,
203
334
  getCachedAutoSearch: getSessionCachedAutoSearch,
204
335
  resolveSessionId: (context, payload) => resolveSessionId(context, payload),
205
336
  normalizeIncomingMessage,
@@ -257,6 +388,15 @@ function sanitizeForLogging(obj) {
257
388
  }
258
389
  return sanitized;
259
390
  }
391
+ function logLifecycle(event, details = {}) {
392
+ const payload = sanitizeForLogging({
393
+ event,
394
+ plugin: PLUGIN_ID,
395
+ ts: new Date().toISOString(),
396
+ ...details,
397
+ });
398
+ logger.info(`[Lifecycle] ${JSON.stringify(payload)}`);
399
+ }
260
400
  function asRecord(value) {
261
401
  if (typeof value === "object" && value !== null) {
262
402
  return value;
@@ -381,7 +521,7 @@ function compareVersions(a, b) {
381
521
  }
382
522
  async function checkOpenClawVersion() {
383
523
  try {
384
- const version = process.env.OPENCLAW_VERSION;
524
+ const version = (0, runtime_env_1.getEnvValue)("OPENCLAW_VERSION");
385
525
  if (version) {
386
526
  if (compareVersions(version, MIN_OPENCLAW_VERSION) < 0) {
387
527
  throw new Error(`Incompatible OpenClaw version: ${version}. Minimum required: ${MIN_OPENCLAW_VERSION}`);
@@ -415,13 +555,19 @@ function findProjectRoot() {
415
555
  throw new Error("Cannot find project root directory");
416
556
  }
417
557
  function findOpenClawConfig() {
558
+ const explicitConfigPath = (0, runtime_env_1.getEnvValue)("OPENCLAW_CONFIG_PATH");
559
+ const stateDir = (0, runtime_env_1.getEnvValue)("OPENCLAW_STATE_DIR");
560
+ const basePath = (0, runtime_env_1.getEnvValue)("OPENCLAW_BASE_PATH");
561
+ const homePath = (0, runtime_env_1.getHomeDir)();
418
562
  const possiblePaths = [
563
+ explicitConfigPath,
564
+ stateDir ? path.join(stateDir, "openclaw.json") : "",
565
+ basePath ? path.join(basePath, "openclaw.json") : "",
419
566
  path.join(process.cwd(), "openclaw.json"),
420
- path.join(process.env.USERPROFILE || process.env.HOME || "", ".openclaw", "openclaw.json"),
421
- path.join(process.env.OPENCLAW_BASE_PATH || "", "openclaw.json"),
567
+ homePath ? path.join(homePath, ".openclaw", "openclaw.json") : "",
422
568
  ];
423
569
  for (const p of possiblePaths) {
424
- if (fs.existsSync(p)) {
570
+ if (p && fs.existsSync(p)) {
425
571
  return p;
426
572
  }
427
573
  }
@@ -476,6 +622,8 @@ function startAutoReflectScheduler() {
476
622
  if (!config?.autoReflect || autoReflectInterval) {
477
623
  return;
478
624
  }
625
+ const intervalMinutes = Math.max(5, Math.floor(config.autoReflectIntervalMinutes ?? 30));
626
+ const intervalMs = intervalMinutes * 60 * 1000;
479
627
  autoReflectInterval = setInterval(() => {
480
628
  if (!isEnabled) {
481
629
  return;
@@ -492,7 +640,7 @@ function startAutoReflectScheduler() {
492
640
  if (marker === lastAutoReflectArchiveMarker) {
493
641
  return;
494
642
  }
495
- if (now - lastAutoReflectRunAt < 5 * 60 * 1000) {
643
+ if (now - lastAutoReflectRunAt < intervalMs) {
496
644
  return;
497
645
  }
498
646
  resolveEngine().reflectMemory({}, schedulerContext)
@@ -507,7 +655,7 @@ function startAutoReflectScheduler() {
507
655
  }
508
656
  })
509
657
  .catch(error => logger.warn(`Auto-reflect failed: ${String(error)}`));
510
- }, 5 * 60 * 1000);
658
+ }, intervalMs);
511
659
  }
512
660
  function stopAutoReflectScheduler() {
513
661
  if (autoReflectInterval) {
@@ -523,6 +671,12 @@ function validateConfig(cfg) {
523
671
  if (!cfg.embedding?.apiKey || !cfg.embedding?.baseURL) {
524
672
  errors.push("embedding.apiKey and embedding.baseURL are required. Please configure third-party embedding endpoint credentials.");
525
673
  }
674
+ if (typeof cfg.embedding?.timeoutMs === "number" && (!Number.isFinite(cfg.embedding.timeoutMs) || cfg.embedding.timeoutMs < 1000)) {
675
+ errors.push("embedding.timeoutMs must be a number >= 1000.");
676
+ }
677
+ if (typeof cfg.embedding?.maxRetries === "number" && (!Number.isFinite(cfg.embedding.maxRetries) || cfg.embedding.maxRetries < 1 || cfg.embedding.maxRetries > 8)) {
678
+ errors.push("embedding.maxRetries must be between 1 and 8.");
679
+ }
526
680
  if (!cfg.llm?.provider || !cfg.llm?.model) {
527
681
  errors.push("llm.provider and llm.model are required. Please configure them in openclaw.json");
528
682
  }
@@ -535,6 +689,88 @@ function validateConfig(cfg) {
535
689
  if (!cfg.reranker?.apiKey || !cfg.reranker?.baseURL) {
536
690
  errors.push("reranker.apiKey and reranker.baseURL are required. Please configure third-party reranker endpoint credentials.");
537
691
  }
692
+ if (typeof cfg.autoReflectIntervalMinutes === "number" && (!Number.isFinite(cfg.autoReflectIntervalMinutes) || cfg.autoReflectIntervalMinutes < 5)) {
693
+ errors.push("autoReflectIntervalMinutes must be a number >= 5.");
694
+ }
695
+ if (cfg.readFusion && typeof cfg.readFusion.maxCandidates === "number" && (!Number.isFinite(cfg.readFusion.maxCandidates) || cfg.readFusion.maxCandidates < 2)) {
696
+ errors.push("readFusion.maxCandidates must be a number >= 2.");
697
+ }
698
+ if (cfg.readFusion?.channelWeights) {
699
+ for (const [key, value] of Object.entries(cfg.readFusion.channelWeights)) {
700
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
701
+ errors.push(`readFusion.channelWeights.${key} must be a number > 0.`);
702
+ break;
703
+ }
704
+ }
705
+ }
706
+ if (cfg.readFusion?.channelTopK) {
707
+ for (const [key, value] of Object.entries(cfg.readFusion.channelTopK)) {
708
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 1) {
709
+ errors.push(`readFusion.channelTopK.${key} must be a number >= 1.`);
710
+ break;
711
+ }
712
+ }
713
+ }
714
+ if (typeof cfg.readFusion?.minLexicalHits === "number" && (!Number.isFinite(cfg.readFusion.minLexicalHits) || cfg.readFusion.minLexicalHits < 0)) {
715
+ errors.push("readFusion.minLexicalHits must be a number >= 0.");
716
+ }
717
+ if (typeof cfg.readFusion?.minSemanticHits === "number" && (!Number.isFinite(cfg.readFusion.minSemanticHits) || cfg.readFusion.minSemanticHits < 0)) {
718
+ errors.push("readFusion.minSemanticHits must be a number >= 0.");
719
+ }
720
+ if (cfg.readFusion?.lengthNorm) {
721
+ const ln = cfg.readFusion.lengthNorm;
722
+ if (typeof ln.pivotChars === "number" && (!Number.isFinite(ln.pivotChars) || ln.pivotChars <= 0)) {
723
+ errors.push("readFusion.lengthNorm.pivotChars must be > 0.");
724
+ }
725
+ if (typeof ln.strength === "number" && (!Number.isFinite(ln.strength) || ln.strength <= 0)) {
726
+ errors.push("readFusion.lengthNorm.strength must be > 0.");
727
+ }
728
+ if (typeof ln.minFactor === "number" && (!Number.isFinite(ln.minFactor) || ln.minFactor <= 0 || ln.minFactor > 1)) {
729
+ errors.push("readFusion.lengthNorm.minFactor must be within (0,1].");
730
+ }
731
+ }
732
+ if (cfg.vectorChunking) {
733
+ if (typeof cfg.vectorChunking.chunkSize === "number" && (!Number.isFinite(cfg.vectorChunking.chunkSize) || cfg.vectorChunking.chunkSize < 200)) {
734
+ errors.push("vectorChunking.chunkSize must be >= 200.");
735
+ }
736
+ if (typeof cfg.vectorChunking.chunkOverlap === "number" && (!Number.isFinite(cfg.vectorChunking.chunkOverlap) || cfg.vectorChunking.chunkOverlap < 0)) {
737
+ errors.push("vectorChunking.chunkOverlap must be >= 0.");
738
+ }
739
+ if (typeof cfg.vectorChunking.chunkSize === "number" &&
740
+ typeof cfg.vectorChunking.chunkOverlap === "number" &&
741
+ cfg.vectorChunking.chunkOverlap >= cfg.vectorChunking.chunkSize) {
742
+ errors.push("vectorChunking.chunkOverlap must be smaller than chunkSize.");
743
+ }
744
+ }
745
+ if (cfg.memoryDecay) {
746
+ if (typeof cfg.memoryDecay.minFloor === "number" && (!Number.isFinite(cfg.memoryDecay.minFloor) || cfg.memoryDecay.minFloor < 0 || cfg.memoryDecay.minFloor > 1)) {
747
+ errors.push("memoryDecay.minFloor must be within [0,1].");
748
+ }
749
+ if (typeof cfg.memoryDecay.defaultHalfLifeDays === "number" && (!Number.isFinite(cfg.memoryDecay.defaultHalfLifeDays) || cfg.memoryDecay.defaultHalfLifeDays <= 0)) {
750
+ errors.push("memoryDecay.defaultHalfLifeDays must be > 0.");
751
+ }
752
+ const mapping = cfg.memoryDecay.halfLifeByEventType;
753
+ if (mapping && typeof mapping === "object") {
754
+ for (const [key, value] of Object.entries(mapping)) {
755
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
756
+ errors.push(`memoryDecay.halfLifeByEventType.${key} must be > 0.`);
757
+ break;
758
+ }
759
+ }
760
+ }
761
+ const anti = cfg.memoryDecay.antiDecay;
762
+ if (anti) {
763
+ if (typeof anti.maxBoost === "number" && (!Number.isFinite(anti.maxBoost) || anti.maxBoost < 1)) {
764
+ errors.push("memoryDecay.antiDecay.maxBoost must be >= 1.");
765
+ }
766
+ if (typeof anti.hitWeight === "number" && (!Number.isFinite(anti.hitWeight) || anti.hitWeight < 0)) {
767
+ errors.push("memoryDecay.antiDecay.hitWeight must be >= 0.");
768
+ }
769
+ if (typeof anti.recentWindowDays === "number" && (!Number.isFinite(anti.recentWindowDays) || anti.recentWindowDays <= 0)) {
770
+ errors.push("memoryDecay.antiDecay.recentWindowDays must be > 0.");
771
+ }
772
+ }
773
+ }
538
774
  return errors;
539
775
  }
540
776
  function getApiHostAndPort() {
@@ -720,14 +956,14 @@ async function startPythonServiceInternal() {
720
956
  }
721
957
  logger.info("Starting Cortex Memory Python service...");
722
958
  const env = {
723
- ...process.env,
959
+ ...(0, runtime_env_1.getProcessEnvCopy)(),
724
960
  CORTEX_MEMORY_EMBEDDING_PROVIDER: config.embedding.provider,
725
961
  CORTEX_MEMORY_EMBEDDING_MODEL: config.embedding.model,
726
962
  CORTEX_MEMORY_LLM_PROVIDER: config.llm.provider,
727
963
  CORTEX_MEMORY_LLM_MODEL: config.llm.model,
728
964
  CORTEX_MEMORY_RERANKER_PROVIDER: config.reranker.provider || "",
729
965
  CORTEX_MEMORY_RERANKER_MODEL: config.reranker.model,
730
- CORTEX_MEMORY_DB_PATH: config.dbPath || path.join(process.env.USERPROFILE || process.env.HOME || "", ".openclaw", "agents", "main", "lancedb_store"),
966
+ CORTEX_MEMORY_DB_PATH: config.dbPath || path.join((0, runtime_env_1.getHomeDir)(), ".openclaw", "agents", "main", "lancedb_store"),
731
967
  };
732
968
  if (config.embedding.apiKey) {
733
969
  env.CORTEX_MEMORY_EMBEDDING_API_KEY = config.embedding.apiKey;
@@ -870,7 +1106,7 @@ function stopPythonService() {
870
1106
  stopPythonServiceAsync();
871
1107
  }
872
1108
  function getBaseUrl() {
873
- return config?.apiUrl ?? "http://127.0.0.1:8765";
1109
+ return config?.apiUrl ?? "http://localhost:8765";
874
1110
  }
875
1111
  async function waitForService(maxAttempts = 30) {
876
1112
  const apiUrl = getBaseUrl();
@@ -1365,10 +1601,18 @@ async function onTimerPythonHandler(payload, _context) {
1365
1601
  }
1366
1602
  }
1367
1603
  async function onMessageHandler(payload, context) {
1368
- await resolveEngine().onMessage(payload, context);
1604
+ const sessionId = resolveSessionId(context, payload);
1605
+ if (isInternalSession(sessionId)) {
1606
+ return;
1607
+ }
1608
+ await runWithTimeout(resolveEngine().onMessage(payload, context), HOOK_GUARD_TIMEOUT_MS, "onMessage hook");
1369
1609
  }
1370
1610
  async function onSessionEndHandler(payload, context) {
1371
- await resolveEngine().onSessionEnd(payload, context);
1611
+ const sessionId = resolveSessionId(context, payload);
1612
+ if (isInternalSession(sessionId)) {
1613
+ return;
1614
+ }
1615
+ await runWithTimeout(resolveEngine().onSessionEnd(payload, context), HOOK_GUARD_TIMEOUT_MS, "onSessionEnd hook");
1372
1616
  }
1373
1617
  async function onTimerHandler(payload, context) {
1374
1618
  await resolveEngine().onTimer(payload, context);
@@ -1497,6 +1741,26 @@ function registerTools() {
1497
1741
  return resolveEngine().syncMemory(args, params.context);
1498
1742
  },
1499
1743
  },
1744
+ {
1745
+ name: "backfill_embeddings",
1746
+ description: "Backfill missing embeddings for active/archive records",
1747
+ parameters: {
1748
+ type: "object",
1749
+ properties: {
1750
+ layer: { type: "string", enum: ["active", "archive", "all"], description: "Target layer to backfill" },
1751
+ batch_size: { type: "integer", description: "Batch size per processing window" },
1752
+ max_retries: { type: "integer", description: "Max retry count for failed records" },
1753
+ retry_failed_only: { type: "boolean", description: "Only retry failed records" },
1754
+ rebuild_mode: { type: "string", enum: ["incremental", "vector_only", "full"], description: "Rebuild mode" },
1755
+ },
1756
+ required: [],
1757
+ additionalProperties: false,
1758
+ },
1759
+ execute: async (params) => {
1760
+ const args = params.args || params;
1761
+ return resolveEngine().backfillEmbeddings(args, params.context);
1762
+ },
1763
+ },
1500
1764
  {
1501
1765
  name: "delete_memory",
1502
1766
  description: "Delete a memory by ID",
@@ -1529,10 +1793,39 @@ function registerTools() {
1529
1793
  },
1530
1794
  ];
1531
1795
  for (const tool of tools) {
1532
- api.registerTool(tool);
1796
+ registerToolCompat(tool);
1533
1797
  registeredTools.push(tool.name);
1534
1798
  }
1535
1799
  }
1800
+ function registerToolCompat(tool) {
1801
+ if (!api)
1802
+ return;
1803
+ const execute = async (params) => tool.execute({
1804
+ args: params?.args || {},
1805
+ context: params.context,
1806
+ });
1807
+ const handler = async (...params) => {
1808
+ const first = params[0];
1809
+ const second = params[1];
1810
+ if (first && typeof first === "object" && "context" in first) {
1811
+ return execute({
1812
+ args: first.args || {},
1813
+ context: first.context,
1814
+ });
1815
+ }
1816
+ return execute({
1817
+ args: first || {},
1818
+ context: (second || {}),
1819
+ });
1820
+ };
1821
+ api.registerTool({
1822
+ name: tool.name,
1823
+ description: tool.description,
1824
+ parameters: tool.parameters,
1825
+ execute,
1826
+ handler,
1827
+ });
1828
+ }
1536
1829
  function unregisterTools() {
1537
1830
  if (!api || !api.unregisterTool)
1538
1831
  return;
@@ -1637,6 +1930,7 @@ async function enable() {
1637
1930
  return;
1638
1931
  }
1639
1932
  logger.info("Enabling Cortex Memory plugin...");
1933
+ logLifecycle("enable_start");
1640
1934
  try {
1641
1935
  unregisterFallbackTools();
1642
1936
  if (shouldUsePythonRuntime()) {
@@ -1648,10 +1942,12 @@ async function enable() {
1648
1942
  registerHooks();
1649
1943
  startAutoReflectScheduler();
1650
1944
  logger.info("Cortex Memory plugin enabled successfully");
1945
+ logLifecycle("enable_success", { registeredTools: registeredTools.length, registeredHooks: registeredHooks.length });
1651
1946
  }
1652
1947
  catch (error) {
1653
1948
  const message = error instanceof Error ? error.message : String(error);
1654
1949
  logger.error(`Failed to enable Cortex Memory plugin: ${message}`);
1950
+ logLifecycle("enable_failed", { error: message });
1655
1951
  throw error;
1656
1952
  }
1657
1953
  }
@@ -1661,6 +1957,7 @@ async function disable() {
1661
1957
  return;
1662
1958
  }
1663
1959
  logger.info("Disabling Cortex Memory plugin...");
1960
+ logLifecycle("disable_start");
1664
1961
  unregisterHooks();
1665
1962
  unregisterTools();
1666
1963
  unregisterFallbackTools();
@@ -1673,13 +1970,15 @@ async function disable() {
1673
1970
  if (config?.fallbackToBuiltin && builtinMemory) {
1674
1971
  logger.info("Falling back to OpenClaw builtin memory system");
1675
1972
  registerFallbackTools();
1973
+ logLifecycle("fallback_enabled", { fallbackTools: registeredFallbackTools.length });
1676
1974
  }
1677
1975
  logger.info("Cortex Memory plugin disabled successfully");
1976
+ logLifecycle("disable_success", { fallbackEnabled: registeredFallbackTools.length > 0 });
1678
1977
  }
1679
1978
  function registerFallbackTools() {
1680
1979
  if (!api || !builtinMemory)
1681
1980
  return;
1682
- api.registerTool({
1981
+ registerToolCompat({
1683
1982
  name: "search_memory",
1684
1983
  description: "Search memory (using builtin system - Cortex Memory disabled)",
1685
1984
  parameters: {
@@ -1691,10 +1990,10 @@ function registerFallbackTools() {
1691
1990
  required: ["query"],
1692
1991
  additionalProperties: false,
1693
1992
  },
1694
- execute: async ({ args, context }) => searchMemoryWithFallback(args, context),
1993
+ execute: async ({ args, context }) => searchMemoryWithFallback((args || {}), context),
1695
1994
  });
1696
1995
  registeredFallbackTools.push("search_memory");
1697
- api.registerTool({
1996
+ registerToolCompat({
1698
1997
  name: "store_event",
1699
1998
  description: "Store event (using builtin system - Cortex Memory disabled)",
1700
1999
  parameters: {
@@ -1705,10 +2004,10 @@ function registerFallbackTools() {
1705
2004
  required: ["summary"],
1706
2005
  additionalProperties: false,
1707
2006
  },
1708
- execute: async ({ args, context }) => storeEventWithFallback(args, context),
2007
+ execute: async ({ args, context }) => storeEventWithFallback((args || {}), context),
1709
2008
  });
1710
2009
  registeredFallbackTools.push("store_event");
1711
- api.registerTool({
2010
+ registerToolCompat({
1712
2011
  name: "cortex_memory_status",
1713
2012
  description: "Get the current status of the Cortex Memory plugin",
1714
2013
  parameters: {
@@ -1717,7 +2016,7 @@ function registerFallbackTools() {
1717
2016
  required: [],
1718
2017
  additionalProperties: false,
1719
2018
  },
1720
- execute: async ({ args, context }) => getPluginStatus(args, context),
2019
+ execute: async ({ args, context }) => getPluginStatus(args || {}, context),
1721
2020
  });
1722
2021
  registeredFallbackTools.push("cortex_memory_status");
1723
2022
  }
@@ -1742,6 +2041,7 @@ function getStatus() {
1742
2041
  }
1743
2042
  async function unregister() {
1744
2043
  logger.info("Unregistering Cortex Memory plugin...");
2044
+ logLifecycle("unregister_start");
1745
2045
  stopConfigWatcher();
1746
2046
  stopAutoReflectScheduler();
1747
2047
  unregisterHooks();
@@ -1768,6 +2068,7 @@ async function unregister() {
1768
2068
  stopAutoReflectScheduler();
1769
2069
  configPath = null;
1770
2070
  logger.info("Cortex Memory plugin unregistered successfully");
2071
+ logLifecycle("unregister_success");
1771
2072
  }
1772
2073
  function register(pluginApi, userConfig) {
1773
2074
  if (isInitializing || isRegistered) {
@@ -1803,9 +2104,41 @@ function register(pluginApi, userConfig) {
1803
2104
  dbPath: effectiveConfig.dbPath,
1804
2105
  autoSync: effectiveConfig.autoSync ?? defaultConfig.autoSync,
1805
2106
  autoReflect: effectiveConfig.autoReflect ?? defaultConfig.autoReflect,
2107
+ autoReflectIntervalMinutes: effectiveConfig.autoReflectIntervalMinutes ?? defaultConfig.autoReflectIntervalMinutes,
2108
+ readFusion: {
2109
+ enabled: effectiveConfig.readFusion?.enabled ?? defaultConfig.readFusion?.enabled,
2110
+ maxCandidates: effectiveConfig.readFusion?.maxCandidates ?? defaultConfig.readFusion?.maxCandidates,
2111
+ authoritative: effectiveConfig.readFusion?.authoritative ?? defaultConfig.readFusion?.authoritative,
2112
+ channelWeights: effectiveConfig.readFusion?.channelWeights ?? defaultConfig.readFusion?.channelWeights,
2113
+ channelTopK: effectiveConfig.readFusion?.channelTopK ?? defaultConfig.readFusion?.channelTopK,
2114
+ minLexicalHits: effectiveConfig.readFusion?.minLexicalHits ?? defaultConfig.readFusion?.minLexicalHits,
2115
+ minSemanticHits: effectiveConfig.readFusion?.minSemanticHits ?? defaultConfig.readFusion?.minSemanticHits,
2116
+ lengthNorm: {
2117
+ enabled: effectiveConfig.readFusion?.lengthNorm?.enabled ?? defaultConfig.readFusion?.lengthNorm?.enabled,
2118
+ pivotChars: effectiveConfig.readFusion?.lengthNorm?.pivotChars ?? defaultConfig.readFusion?.lengthNorm?.pivotChars,
2119
+ strength: effectiveConfig.readFusion?.lengthNorm?.strength ?? defaultConfig.readFusion?.lengthNorm?.strength,
2120
+ minFactor: effectiveConfig.readFusion?.lengthNorm?.minFactor ?? defaultConfig.readFusion?.lengthNorm?.minFactor,
2121
+ },
2122
+ },
2123
+ vectorChunking: {
2124
+ chunkSize: effectiveConfig.vectorChunking?.chunkSize ?? defaultConfig.vectorChunking?.chunkSize,
2125
+ chunkOverlap: effectiveConfig.vectorChunking?.chunkOverlap ?? defaultConfig.vectorChunking?.chunkOverlap,
2126
+ },
2127
+ memoryDecay: {
2128
+ enabled: effectiveConfig.memoryDecay?.enabled ?? defaultConfig.memoryDecay?.enabled,
2129
+ minFloor: effectiveConfig.memoryDecay?.minFloor ?? defaultConfig.memoryDecay?.minFloor,
2130
+ defaultHalfLifeDays: effectiveConfig.memoryDecay?.defaultHalfLifeDays ?? defaultConfig.memoryDecay?.defaultHalfLifeDays,
2131
+ halfLifeByEventType: effectiveConfig.memoryDecay?.halfLifeByEventType ?? defaultConfig.memoryDecay?.halfLifeByEventType,
2132
+ antiDecay: {
2133
+ enabled: effectiveConfig.memoryDecay?.antiDecay?.enabled ?? defaultConfig.memoryDecay?.antiDecay?.enabled,
2134
+ maxBoost: effectiveConfig.memoryDecay?.antiDecay?.maxBoost ?? defaultConfig.memoryDecay?.antiDecay?.maxBoost,
2135
+ hitWeight: effectiveConfig.memoryDecay?.antiDecay?.hitWeight ?? defaultConfig.memoryDecay?.antiDecay?.hitWeight,
2136
+ recentWindowDays: effectiveConfig.memoryDecay?.antiDecay?.recentWindowDays ?? defaultConfig.memoryDecay?.antiDecay?.recentWindowDays,
2137
+ },
2138
+ },
1806
2139
  enabled: effectiveConfig.enabled ?? defaultConfig.enabled,
1807
2140
  fallbackToBuiltin: effectiveConfig.fallbackToBuiltin ?? defaultConfig.fallbackToBuiltin,
1808
- apiUrl: effectiveConfig.apiUrl ?? "http://127.0.0.1:8765",
2141
+ apiUrl: effectiveConfig.apiUrl ?? "http://localhost:8765",
1809
2142
  engineMode: "ts",
1810
2143
  };
1811
2144
  memoryEngine = null;
@@ -1840,6 +2173,11 @@ function register(pluginApi, userConfig) {
1840
2173
  isRegistered = true;
1841
2174
  logger.info("Cortex Memory plugin registered successfully");
1842
2175
  logger.info(`Cortex Memory engine mode: ${resolveEngine().mode}`);
2176
+ logLifecycle("register_success", {
2177
+ engineMode: config.engineMode,
2178
+ enabled: isEnabled,
2179
+ hasBuiltinFallback: Boolean(builtinMemory),
2180
+ });
1843
2181
  if (isEnabled) {
1844
2182
  registerTools();
1845
2183
  registerHooks();
@@ -1853,11 +2191,13 @@ function register(pluginApi, userConfig) {
1853
2191
  logger.info("Falling back to builtin memory");
1854
2192
  isEnabled = false;
1855
2193
  registerFallbackTools();
2194
+ logLifecycle("fallback_after_init_error", { fallbackTools: registeredFallbackTools.length, error: message });
1856
2195
  }
1857
2196
  });
1858
2197
  }
1859
2198
  else if (config?.fallbackToBuiltin && builtinMemory) {
1860
2199
  registerFallbackTools();
2200
+ logLifecycle("fallback_registered_on_start", { fallbackTools: registeredFallbackTools.length });
1861
2201
  }
1862
2202
  }
1863
2203
  async function initializeAsync() {