mr-memory 3.7.5 → 3.7.7

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/index.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * BYOK — provider API keys never touch MemoryRouter.
9
9
  */
10
10
  import { readFile, lstat, writeFile, copyFile, mkdir } from "node:fs/promises";
11
- import { deleteMemories, getMemoryStats, ingestMemory, prepareMemory, searchMemory } from "./memoryrouter-api.js";
11
+ import { deleteMemories, getMemoryStats, ingestMemory, prepareMemory, searchMemory, warmupMemory } from "./memoryrouter-api.js";
12
12
  import { join, resolve, relative, isAbsolute, sep, dirname } from "node:path";
13
13
  import os from "node:os";
14
14
  const DEFAULT_ENDPOINT = "https://api.memoryrouter.ai";
@@ -267,7 +267,7 @@ const WORKSPACE_FILES = [
267
267
  "TOOLS.md", "AGENTS.md", "SOUL.md", "BOOTSTRAP.md",
268
268
  ];
269
269
  function openClawConfigPath() {
270
- return process.env.OPENCLAW_CONFIG || join(os.homedir(), ".openclaw", "openclaw.json");
270
+ return process.env.OPENCLAW_CONFIG_PATH || process.env.OPENCLAW_CONFIG || join(os.homedir(), ".openclaw", "openclaw.json");
271
271
  }
272
272
  async function readOpenClawConfigFile() {
273
273
  try {
@@ -292,6 +292,44 @@ async function writeOpenClawConfigFile(config) {
292
292
  await writeFile(configPath, `${JSON.stringify(config, null, 2)}
293
293
  `, "utf8");
294
294
  }
295
+ // ──────────────────────────────────────────────────────
296
+ // Full-replace: disable OpenClaw's native memory search so MemoryRouter
297
+ // owns the memory_search tool (no name conflict, no native embeddings 429).
298
+ // memoryFlush (durable MEMORY.md writes) is intentionally left untouched.
299
+ // Global + subagents: writes agents.defaults.memorySearch.enabled.
300
+ // ──────────────────────────────────────────────────────
301
+ async function setNativeMemorySearchEnabled(enabled) {
302
+ const config = await readOpenClawConfigFile();
303
+ config.agents ??= {};
304
+ config.agents.defaults ??= {};
305
+ config.agents.defaults.memorySearch ??= {};
306
+ if (config.agents.defaults.memorySearch.enabled === enabled) return false; // no-op
307
+ config.agents.defaults.memorySearch.enabled = enabled;
308
+ await writeOpenClawConfigFile(config);
309
+ return true;
310
+ }
311
+
312
+ // Boot-time self-heal: if a key is configured but native search is still on,
313
+ // flip it off so already-installed users are fixed on restart after updating
314
+ // (no need to re-run `openclaw mr <key>`).
315
+ async function selfHealNativeMemorySearch(api) {
316
+ try {
317
+ const config = await readOpenClawConfigFile();
318
+ const ms = config?.agents?.defaults?.memorySearch;
319
+ if (ms?.enabled === false) return; // already disabled — nothing to do
320
+ const changed = await setNativeMemorySearchEnabled(false);
321
+ if (changed) {
322
+ api.logger.info?.(
323
+ "memoryrouter: disabled native memory search (full-replace). Restart gateway to apply.",
324
+ );
325
+ }
326
+ } catch (err) {
327
+ api.logger.info?.(
328
+ `memoryrouter: self-heal skipped — ${err instanceof Error ? err.message : String(err)}`,
329
+ );
330
+ }
331
+ }
332
+
295
333
  async function updateOpenClawPluginEntry(api, patch) {
296
334
  const config = await readOpenClawConfigFile();
297
335
  config.plugins ??= {};
@@ -319,6 +357,47 @@ async function setPluginEnabled(api, enabled) {
319
357
  }
320
358
  await updateOpenClawPluginEntry(api, { enabled });
321
359
  }
360
+ async function mergePluginConfig(api, patch) {
361
+ const current = await readOpenClawConfigFile();
362
+ const existing = current.plugins?.entries?.[api.id]?.config;
363
+ await setPluginConfig(api, {
364
+ ...(existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {}),
365
+ ...patch,
366
+ });
367
+ }
368
+ // Keep mr-memory as a first-class plugin config entry without clobbering user settings.
369
+ // OpenClaw doctor validates plugin entries against discoverable manifests, so the entry
370
+ // must be present, enabled, and allowlisted when an allowlist exists.
371
+ async function ensureDoctorSafePluginEntry(api) {
372
+ try {
373
+ const config = await readOpenClawConfigFile();
374
+ const plugins = config.plugins ?? {};
375
+ const entries = plugins.entries ?? {};
376
+ const existingEntry = entries[api.id] && typeof entries[api.id] === "object" && !Array.isArray(entries[api.id])
377
+ ? entries[api.id]
378
+ : {};
379
+ const nextPlugins = {
380
+ ...plugins,
381
+ entries: {
382
+ ...entries,
383
+ [api.id]: {
384
+ ...existingEntry,
385
+ enabled: true,
386
+ },
387
+ },
388
+ };
389
+ if (Array.isArray(plugins.allow) && !plugins.allow.includes(api.id)) {
390
+ nextPlugins.allow = [...plugins.allow, api.id];
391
+ }
392
+ const next = { ...config, plugins: nextPlugins };
393
+ if (JSON.stringify(next.plugins) === JSON.stringify(config.plugins))
394
+ return;
395
+ await writeOpenClawConfigFile(next);
396
+ }
397
+ catch (err) {
398
+ api.logger.info?.(`memoryrouter: doctor-safe registration skipped — ${err instanceof Error ? err.message : String(err)}`);
399
+ }
400
+ }
322
401
  /**
323
402
  * Read all workspace files and return as a single text blob for token counting.
324
403
  */
@@ -396,6 +475,11 @@ const memoryRouterPlugin = {
396
475
  return memoryKey;
397
476
  }
398
477
  const hasAnyKey = !!(memoryKey || (agentKeys && Object.keys(agentKeys).length > 0));
478
+ void (async () => {
479
+ await ensureDoctorSafePluginEntry(api);
480
+ if (hasAnyKey)
481
+ await selfHealNativeMemorySearch(api);
482
+ })();
399
483
  if (hasAnyKey) {
400
484
  if (memoryKey)
401
485
  api.logger.info?.(`memoryrouter: active (key: ${memoryKey.slice(0, 6)}...)`);
@@ -563,6 +647,8 @@ If setting up MemoryRouter for another agent:
563
647
  if (data.context) {
564
648
  const elapsed = Date.now() - startMs;
565
649
  api.logger.info?.(`memoryrouter: injected ${data.memories_found || 0} memories (${data.memory_tokens || 0} tokens, ${elapsed}ms) [llm_input]`);
650
+ if (elapsed > 2000 && data.metrics)
651
+ log(`memoryrouter: prepare metrics ${JSON.stringify(data.metrics)}`);
566
652
  const wrapped = wrapForInjection(data.context);
567
653
  return { appendSystemContext: wrapped };
568
654
  }
@@ -664,6 +750,8 @@ If setting up MemoryRouter for another agent:
664
750
  if (data.context) {
665
751
  const elapsed = Date.now() - startMs;
666
752
  api.logger.info?.(`memoryrouter: injected ${data.memories_found || 0} memories (${data.memory_tokens || 0} tokens, ${elapsed}ms)`);
753
+ if (elapsed > 2000 && data.metrics)
754
+ log(`memoryrouter: prepare metrics ${JSON.stringify(data.metrics)}`);
667
755
  const wrapped = wrapForInjection(data.context);
668
756
  return { appendSystemContext: wrapped };
669
757
  }
@@ -879,8 +967,14 @@ If setting up MemoryRouter for another agent:
879
967
  return;
880
968
  }
881
969
  try {
882
- await setPluginConfig(api, { key });
970
+ await mergePluginConfig(api, { key });
883
971
  await setPluginEnabled(api, true);
972
+ try {
973
+ const changed = await setNativeMemorySearchEnabled(false);
974
+ if (changed) console.log(` • Native memory search disabled — MemoryRouter now owns memory_search`);
975
+ } catch (err) {
976
+ console.error(` ⚠ Could not disable native memory search: ${err instanceof Error ? err.message : String(err)}`);
977
+ }
884
978
  console.log(`✓ MemoryRouter enabled. Key: ${key.slice(0, 6)}...${key.slice(-3)}`);
885
979
  console.log(` • memory_search + memory_get tools registered via plugin`);
886
980
  console.log(`\nRun: openclaw mr upload to upload your memories`);
@@ -913,6 +1007,13 @@ If setting up MemoryRouter for another agent:
913
1007
  // This avoids the catch-22 where disabling the plugin removes the `mr` command
914
1008
  await setPluginConfig(api, {});
915
1009
  // Note: we intentionally do NOT call setPluginEnabled(api, false)
1010
+ // Restore native memory search so the user isn't left with NO memory search.
1011
+ try {
1012
+ const changed = await setNativeMemorySearchEnabled(true);
1013
+ if (changed) console.log(" • Native memory search restored");
1014
+ } catch (err) {
1015
+ console.error(` ⚠ Could not restore native memory search: ${err instanceof Error ? err.message : String(err)}`);
1016
+ }
916
1017
  console.log("✓ MemoryRouter disabled (key cleared).");
917
1018
  console.log(" • CLI still available — run `openclaw mr <key>` to re-enable");
918
1019
  }
@@ -942,7 +1043,7 @@ If setting up MemoryRouter for another agent:
942
1043
  return;
943
1044
  }
944
1045
  try {
945
- await setPluginConfig(api, { key: memoryKey, endpoint: cfg?.endpoint, density: name });
1046
+ await mergePluginConfig(api, { key: memoryKey, density: name });
946
1047
  console.log(`✓ Memory density set to ${name}`);
947
1048
  }
948
1049
  catch (err) {
@@ -960,11 +1061,8 @@ If setting up MemoryRouter for another agent:
960
1061
  }
961
1062
  const newLogging = !logging;
962
1063
  try {
963
- await setPluginConfig(api, {
1064
+ await mergePluginConfig(api, {
964
1065
  key: memoryKey,
965
- endpoint: cfg?.endpoint,
966
- density,
967
- mode,
968
1066
  logging: newLogging,
969
1067
  });
970
1068
  console.log(`✓ Logging ${newLogging ? "ON" : "OFF"} (restart gateway to apply)`);
@@ -1117,8 +1215,17 @@ If setting up MemoryRouter for another agent:
1117
1215
  start: () => {
1118
1216
  if (memoryKey)
1119
1217
  api.logger.info?.(`memoryrouter: active (key: ${memoryKey.slice(0, 6)}...)`);
1218
+ if (hasAnyKey && !warmupTimer) {
1219
+ pingWarmup("startup");
1220
+ warmupTimer = setInterval(() => pingWarmup("keepalive"), 4 * 60 * 1000);
1221
+ warmupTimer.unref?.();
1222
+ }
1120
1223
  },
1121
1224
  stop: () => {
1225
+ if (warmupTimer) {
1226
+ clearInterval(warmupTimer);
1227
+ warmupTimer = undefined;
1228
+ }
1122
1229
  api.logger.info?.("memoryrouter: stopped");
1123
1230
  },
1124
1231
  });
package/index.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import { readFile, readdir, stat, lstat, writeFile, copyFile, mkdir } from "node:fs/promises";
12
- import { deleteMemories, getMemoryStats, ingestMemory, prepareMemory, searchMemory } from "./memoryrouter-api.js";
12
+ import { deleteMemories, getMemoryStats, ingestMemory, prepareMemory, searchMemory, warmupMemory } from "./memoryrouter-api.js";
13
13
  import { join, resolve, relative, isAbsolute, sep, dirname } from "node:path";
14
14
  import path from "node:path";
15
15
  import os from "node:os";
@@ -296,7 +296,7 @@ type CompatApi = OpenClawPluginApi & {
296
296
  type JsonObject = Record<string, any>;
297
297
 
298
298
  function openClawConfigPath(): string {
299
- return process.env.OPENCLAW_CONFIG || join(os.homedir(), ".openclaw", "openclaw.json");
299
+ return process.env.OPENCLAW_CONFIG_PATH || process.env.OPENCLAW_CONFIG || join(os.homedir(), ".openclaw", "openclaw.json");
300
300
  }
301
301
 
302
302
  async function readOpenClawConfigFile(): Promise<JsonObject> {
@@ -320,6 +320,44 @@ async function writeOpenClawConfigFile(config: JsonObject): Promise<void> {
320
320
  `, "utf8");
321
321
  }
322
322
 
323
+ // ──────────────────────────────────────────────────────
324
+ // Full-replace: disable OpenClaw's native memory search so MemoryRouter
325
+ // owns the memory_search tool (no name conflict, no native embeddings 429).
326
+ // memoryFlush (durable MEMORY.md writes) is intentionally left untouched.
327
+ // Global + subagents: writes agents.defaults.memorySearch.enabled.
328
+ // ──────────────────────────────────────────────────────
329
+ async function setNativeMemorySearchEnabled(enabled: boolean): Promise<boolean> {
330
+ const config = await readOpenClawConfigFile();
331
+ config.agents ??= {};
332
+ config.agents.defaults ??= {};
333
+ config.agents.defaults.memorySearch ??= {};
334
+ if (config.agents.defaults.memorySearch.enabled === enabled) return false; // no-op
335
+ config.agents.defaults.memorySearch.enabled = enabled;
336
+ await writeOpenClawConfigFile(config);
337
+ return true;
338
+ }
339
+
340
+ // Boot-time self-heal: if a key is configured but native search is still on,
341
+ // flip it off so already-installed users are fixed on restart after updating
342
+ // (no need to re-run `openclaw mr <key>`).
343
+ async function selfHealNativeMemorySearch(api: OpenClawPluginApi): Promise<void> {
344
+ try {
345
+ const config = await readOpenClawConfigFile();
346
+ const ms = config?.agents?.defaults?.memorySearch;
347
+ if (ms?.enabled === false) return; // already disabled — nothing to do
348
+ const changed = await setNativeMemorySearchEnabled(false);
349
+ if (changed) {
350
+ api.logger.info?.(
351
+ "memoryrouter: disabled native memory search (full-replace). Restart gateway to apply.",
352
+ );
353
+ }
354
+ } catch (err) {
355
+ api.logger.info?.(
356
+ `memoryrouter: self-heal skipped — ${err instanceof Error ? err.message : String(err)}`,
357
+ );
358
+ }
359
+ }
360
+
323
361
  async function updateOpenClawPluginEntry(api: OpenClawPluginApi, patch: JsonObject): Promise<void> {
324
362
  const config = await readOpenClawConfigFile();
325
363
  config.plugins ??= {};
@@ -350,6 +388,47 @@ async function setPluginEnabled(api: OpenClawPluginApi, enabled: boolean): Promi
350
388
  await updateOpenClawPluginEntry(api, { enabled });
351
389
  }
352
390
 
391
+ async function mergePluginConfig(api: OpenClawPluginApi, patch: Record<string, unknown>): Promise<void> {
392
+ const current = await readOpenClawConfigFile();
393
+ const existing = current.plugins?.entries?.[api.id]?.config;
394
+ await setPluginConfig(api, {
395
+ ...(existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {}),
396
+ ...patch,
397
+ });
398
+ }
399
+
400
+ // Keep mr-memory as a first-class plugin config entry without clobbering user settings.
401
+ // OpenClaw doctor validates plugin entries against discoverable manifests, so the entry
402
+ // must be present, enabled, and allowlisted when an allowlist exists.
403
+ async function ensureDoctorSafePluginEntry(api: OpenClawPluginApi): Promise<void> {
404
+ try {
405
+ const config = await readOpenClawConfigFile();
406
+ const plugins = config.plugins ?? {};
407
+ const entries = plugins.entries ?? {};
408
+ const existingEntry = entries[api.id] && typeof entries[api.id] === "object" && !Array.isArray(entries[api.id])
409
+ ? entries[api.id]
410
+ : {};
411
+ const nextPlugins: JsonObject = {
412
+ ...plugins,
413
+ entries: {
414
+ ...entries,
415
+ [api.id]: {
416
+ ...existingEntry,
417
+ enabled: true,
418
+ },
419
+ },
420
+ };
421
+ if (Array.isArray(plugins.allow) && !plugins.allow.includes(api.id)) {
422
+ nextPlugins.allow = [...plugins.allow, api.id];
423
+ }
424
+ const next = { ...config, plugins: nextPlugins };
425
+ if (JSON.stringify(next.plugins) === JSON.stringify(config.plugins)) return;
426
+ await writeOpenClawConfigFile(next);
427
+ } catch (err) {
428
+ api.logger.info?.(`memoryrouter: doctor-safe registration skipped — ${err instanceof Error ? err.message : String(err)}`);
429
+ }
430
+ }
431
+
353
432
  /**
354
433
  * Read all workspace files and return as a single text blob for token counting.
355
434
  */
@@ -422,6 +501,40 @@ const memoryRouterPlugin = {
422
501
  }
423
502
 
424
503
  const hasAnyKey = !!(memoryKey || (agentKeys && Object.keys(agentKeys).length > 0));
504
+ let warmupTimer: ReturnType<typeof setInterval> | undefined;
505
+
506
+ function configuredKeys(): string[] {
507
+ const keys = new Set<string>();
508
+ if (memoryKey) keys.add(memoryKey);
509
+ if (agentKeys) {
510
+ for (const key of Object.values(agentKeys)) {
511
+ if (key) keys.add(key);
512
+ }
513
+ }
514
+ return Array.from(keys);
515
+ }
516
+
517
+ function pingWarmup(reason: string): void {
518
+ for (const key of configuredKeys()) {
519
+ warmupMemory(endpoint, key, embeddings)
520
+ .then(async (res) => {
521
+ if (!res.ok) {
522
+ log(`memoryrouter: warmup ${reason} failed (${res.status})`);
523
+ return;
524
+ }
525
+ const data = await res.json().catch(() => null) as any;
526
+ log(`memoryrouter: warmup ${reason} ok (${data?.totalVectors ?? 0} vectors, ${data?.warmupTimeMs ?? "?"}ms)`);
527
+ })
528
+ .catch((err) => {
529
+ log(`memoryrouter: warmup ${reason} error — ${err instanceof Error ? err.message : String(err)}`);
530
+ });
531
+ }
532
+ }
533
+
534
+ void (async () => {
535
+ await ensureDoctorSafePluginEntry(api);
536
+ if (hasAnyKey) await selfHealNativeMemorySearch(api);
537
+ })();
425
538
 
426
539
  if (hasAnyKey) {
427
540
  if (memoryKey) api.logger.info?.(`memoryrouter: active (key: ${memoryKey.slice(0, 6)}...)`);
@@ -592,11 +705,13 @@ If setting up MemoryRouter for another agent:
592
705
  memories_found?: number;
593
706
  tokens_billed?: number;
594
707
  memory_tokens?: number;
708
+ metrics?: Record<string, unknown>;
595
709
  };
596
710
 
597
711
  if (data.context) {
598
712
  const elapsed = Date.now() - startMs;
599
713
  api.logger.info?.(`memoryrouter: injected ${data.memories_found || 0} memories (${data.memory_tokens || 0} tokens, ${elapsed}ms) [llm_input]`);
714
+ if (elapsed > 2000 && data.metrics) log(`memoryrouter: prepare metrics ${JSON.stringify(data.metrics)}`);
600
715
  const wrapped = wrapForInjection(data.context);
601
716
  return { appendSystemContext: wrapped };
602
717
  }
@@ -704,11 +819,13 @@ If setting up MemoryRouter for another agent:
704
819
  memories_found?: number;
705
820
  tokens_billed?: number;
706
821
  memory_tokens?: number;
822
+ metrics?: Record<string, unknown>;
707
823
  };
708
824
 
709
825
  if (data.context) {
710
826
  const elapsed = Date.now() - startMs;
711
827
  api.logger.info?.(`memoryrouter: injected ${data.memories_found || 0} memories (${data.memory_tokens || 0} tokens, ${elapsed}ms)`);
828
+ if (elapsed > 2000 && data.metrics) log(`memoryrouter: prepare metrics ${JSON.stringify(data.metrics)}`);
712
829
  const wrapped = wrapForInjection(data.context);
713
830
  return { appendSystemContext: wrapped };
714
831
  }
@@ -933,8 +1050,14 @@ If setting up MemoryRouter for another agent:
933
1050
  return;
934
1051
  }
935
1052
  try {
936
- await setPluginConfig(api, { key });
1053
+ await mergePluginConfig(api, { key });
937
1054
  await setPluginEnabled(api, true);
1055
+ try {
1056
+ const changed = await setNativeMemorySearchEnabled(false);
1057
+ if (changed) console.log(` • Native memory search disabled — MemoryRouter now owns memory_search`);
1058
+ } catch (err) {
1059
+ console.error(` ⚠ Could not disable native memory search: ${err instanceof Error ? err.message : String(err)}`);
1060
+ }
938
1061
  console.log(`✓ MemoryRouter enabled. Key: ${key.slice(0, 6)}...${key.slice(-3)}`);
939
1062
  console.log(` • memory_search + memory_get tools registered via plugin`);
940
1063
  console.log(`\nRun: openclaw mr upload to upload your memories`);
@@ -966,6 +1089,13 @@ If setting up MemoryRouter for another agent:
966
1089
  // This avoids the catch-22 where disabling the plugin removes the `mr` command
967
1090
  await setPluginConfig(api, {});
968
1091
  // Note: we intentionally do NOT call setPluginEnabled(api, false)
1092
+ // Restore native memory search so the user isn't left with NO memory search.
1093
+ try {
1094
+ const changed = await setNativeMemorySearchEnabled(true);
1095
+ if (changed) console.log(" • Native memory search restored");
1096
+ } catch (err) {
1097
+ console.error(` ⚠ Could not restore native memory search: ${err instanceof Error ? err.message : String(err)}`);
1098
+ }
969
1099
  console.log("✓ MemoryRouter disabled (key cleared).");
970
1100
  console.log(" • CLI still available — run `openclaw mr <key>` to re-enable");
971
1101
  } catch (err) {
@@ -993,7 +1123,7 @@ If setting up MemoryRouter for another agent:
993
1123
  .action(async () => {
994
1124
  if (!memoryKey) { console.error("Not configured. Run: openclaw mr <key>"); return; }
995
1125
  try {
996
- await setPluginConfig(api, { key: memoryKey, endpoint: cfg?.endpoint, density: name });
1126
+ await mergePluginConfig(api, { key: memoryKey, density: name });
997
1127
  console.log(`✓ Memory density set to ${name}`);
998
1128
  } catch (err) {
999
1129
  console.error(`Failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -1011,11 +1141,8 @@ If setting up MemoryRouter for another agent:
1011
1141
  }
1012
1142
  const newLogging = !logging;
1013
1143
  try {
1014
- await setPluginConfig(api, {
1144
+ await mergePluginConfig(api, {
1015
1145
  key: memoryKey,
1016
- endpoint: cfg?.endpoint,
1017
- density,
1018
- mode,
1019
1146
  logging: newLogging,
1020
1147
  });
1021
1148
  console.log(`✓ Logging ${newLogging ? "ON" : "OFF"} (restart gateway to apply)`);
@@ -1184,8 +1311,17 @@ If setting up MemoryRouter for another agent:
1184
1311
  id: "mr-memory",
1185
1312
  start: () => {
1186
1313
  if (memoryKey) api.logger.info?.(`memoryrouter: active (key: ${memoryKey.slice(0, 6)}...)`);
1314
+ if (hasAnyKey && !warmupTimer) {
1315
+ pingWarmup("startup");
1316
+ warmupTimer = setInterval(() => pingWarmup("keepalive"), 4 * 60 * 1000);
1317
+ warmupTimer.unref?.();
1318
+ }
1187
1319
  },
1188
1320
  stop: () => {
1321
+ if (warmupTimer) {
1322
+ clearInterval(warmupTimer);
1323
+ warmupTimer = undefined;
1324
+ }
1189
1325
  api.logger.info?.("memoryrouter: stopped");
1190
1326
  },
1191
1327
  });
@@ -47,3 +47,14 @@ export async function deleteMemories(endpoint, key, embeddings) {
47
47
  headers: { Authorization: `Bearer ${key}` },
48
48
  });
49
49
  }
50
+
51
+ export async function warmupMemory(endpoint, key, embeddings) {
52
+ return fetch(`${endpoint}/v1/memory/warmup`, {
53
+ method: "POST",
54
+ headers: {
55
+ Authorization: `Bearer ${key}`,
56
+ ...(embeddings && { "X-Embedding-Model": embeddings }),
57
+ },
58
+ signal: AbortSignal.timeout(15000),
59
+ });
60
+ }
@@ -58,3 +58,14 @@ export async function deleteMemories(endpoint: string, key: string, embeddings?:
58
58
  headers: { Authorization: `Bearer ${key}` },
59
59
  });
60
60
  }
61
+
62
+ export async function warmupMemory(endpoint: string, key: string, embeddings?: string): Promise<Response> {
63
+ return fetch(`${endpoint}/v1/memory/warmup`, {
64
+ method: "POST",
65
+ headers: {
66
+ Authorization: `Bearer ${key}`,
67
+ ...(embeddings && { "X-Embedding-Model": embeddings }),
68
+ },
69
+ signal: AbortSignal.timeout(15000),
70
+ });
71
+ }
@@ -46,6 +46,14 @@
46
46
  "type": "string",
47
47
  "enum": ["bge", "qwen"],
48
48
  "description": "Embedding model: bge (1024d, default) or qwen (4096d)"
49
+ },
50
+ "agentKeys": {
51
+ "type": "object",
52
+ "additionalProperties": {
53
+ "type": "string",
54
+ "pattern": "^mk[_-]"
55
+ },
56
+ "description": "Optional per-agent MemoryRouter keys, keyed by OpenClaw agent id"
49
57
  }
50
58
  },
51
59
  "required": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mr-memory",
3
- "version": "3.7.5",
3
+ "version": "3.7.7",
4
4
  "description": "mr-memory is the MemoryRouter plugin for OpenClaw — persistent memory across every conversation",
5
5
  "type": "module",
6
6
  "files": [