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

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