@prometheus-ai/memory 0.5.3 → 0.5.8

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 (50) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +4 -4
  3. package/dist/types/config.d.ts +13 -2
  4. package/dist/types/core/beam/store.d.ts +20 -0
  5. package/dist/types/core/embeddings.d.ts +2 -1
  6. package/dist/types/core/extraction/client.d.ts +11 -7
  7. package/dist/types/core/extraction.d.ts +2 -1
  8. package/dist/types/core/fastembed-runtime.d.ts +4 -0
  9. package/dist/types/core/index.d.ts +1 -0
  10. package/dist/types/core/llm-backends.d.ts +2 -0
  11. package/dist/types/core/local-llm.d.ts +8 -3
  12. package/dist/types/core/memory.d.ts +12 -3
  13. package/dist/types/core/query-cache.d.ts +1 -2
  14. package/dist/types/core/runtime-options.d.ts +10 -5
  15. package/dist/types/core/shmr.d.ts +11 -5
  16. package/dist/types/core/vector-index.d.ts +16 -0
  17. package/dist/types/index.d.ts +2 -1
  18. package/package.json +30 -7
  19. package/src/cli.ts +19 -19
  20. package/src/config.ts +98 -68
  21. package/src/core/banks.ts +2 -2
  22. package/src/core/beam/consolidate.ts +34 -5
  23. package/src/core/beam/helpers.ts +21 -28
  24. package/src/core/beam/index.ts +2 -2
  25. package/src/core/beam/recall.ts +98 -25
  26. package/src/core/beam/store.ts +96 -4
  27. package/src/core/binary-vectors.ts +1 -1
  28. package/src/core/content-sanitizer.ts +3 -3
  29. package/src/core/cost-log.ts +1 -1
  30. package/src/core/embeddings.ts +75 -50
  31. package/src/core/extraction/client.ts +44 -20
  32. package/src/core/extraction.ts +10 -9
  33. package/src/core/fastembed-runtime.ts +89 -0
  34. package/src/core/index.ts +1 -0
  35. package/src/core/llm-backends.ts +3 -0
  36. package/src/core/local-llm.ts +81 -43
  37. package/src/core/memory.ts +25 -5
  38. package/src/core/plugins.ts +1 -1
  39. package/src/core/polyphonic-recall.ts +4 -4
  40. package/src/core/query-cache.ts +2 -3
  41. package/src/core/runtime-options.ts +13 -5
  42. package/src/core/shmr.ts +141 -39
  43. package/src/core/streaming.ts +1 -1
  44. package/src/core/triples.ts +3 -3
  45. package/src/core/vector-index.ts +84 -0
  46. package/src/diagnose.ts +2 -2
  47. package/src/dr/recovery.ts +5 -5
  48. package/src/index.ts +1 -1
  49. package/src/mcp-server.ts +2 -2
  50. package/src/mcp-tools.ts +61 -61
package/src/config.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { homedir } from "node:os";
2
2
  import { join } from "node:path";
3
+ import { hostMatchesUrl } from "@prometheus-ai/catalog/hosts";
3
4
  import {
4
5
  type Env,
5
6
  envBool,
@@ -15,13 +16,13 @@ import {
15
16
  export type { Env };
16
17
  export { envBool, envDisabled, envFloat, envInt, envOneOf, envOptionalString, envString, envTruthy };
17
18
 
18
- export const DEFAULT_DATA_DIR = join(homedir(), ".prometheus", "memory", "data");
19
- export const DEFAULT_DB_FILENAME = "prometheus-memory.db";
20
- export const FASTEMBED_CACHE_DIR = join(homedir(), ".prometheus", "cache", "fastembed");
21
- export const MODEL_CACHE_DIR = join(homedir(), ".prometheus", "memory", "models");
19
+ export const DEFAULT_DATA_DIR = join(homedir(), ".hermes", "mnemopi", "data");
20
+ export const DEFAULT_DB_FILENAME = "mnemopi.db";
21
+ export const FASTEMBED_CACHE_DIR = join(homedir(), ".hermes", "cache", "fastembed");
22
+ export const MODEL_CACHE_DIR = join(homedir(), ".hermes", "mnemopi", "models");
22
23
 
23
24
  export const DEFAULT_EMBEDDING_MODEL = "BAAI/bge-small-en-v1.5";
24
- export const DEFAULT_EMBEDDING_API_URL = "https://openrouter.ai/api/v1";
25
+ export const DEFAULT_EMBEDDING_APROMETHEUS_URL = "https://openrouter.ai/api/v1";
25
26
  export const DEFAULT_LLM_MODEL_REPO = "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF";
26
27
  export const DEFAULT_LLM_MODEL_FILE = "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf";
27
28
  export const HOST_LLM_TIMEOUT_SECONDS = 15.0;
@@ -57,7 +58,7 @@ export const VERACITY_WEIGHT_DEFAULTS = {
57
58
  } as const;
58
59
 
59
60
  export function dataDir(env: Env = process.env): string {
60
- return envOptionalString("PROMETHEUS_MEMORY_DATA_DIR", env) ?? DEFAULT_DATA_DIR;
61
+ return envOptionalString("MNEMOPROMETHEUS_DATA_DIR", env) ?? DEFAULT_DATA_DIR;
61
62
  }
62
63
 
63
64
  export function dbPath(env: Env = process.env): string {
@@ -65,141 +66,149 @@ export function dbPath(env: Env = process.env): string {
65
66
  }
66
67
 
67
68
  export function beamOptimizationsEnabled(env: Env = process.env): boolean {
68
- return envTruthy("PROMETHEUS_MEMORY_BEAM_OPTIMIZATIONS", env);
69
+ return envTruthy("MNEMOPROMETHEUS_BEAM_OPTIMIZATIONS", env);
69
70
  }
70
71
 
71
72
  export function embeddingModel(env: Env = process.env): string {
72
- return envString("PROMETHEUS_MEMORY_EMBEDDING_MODEL", DEFAULT_EMBEDDING_MODEL, env);
73
+ return envString("MNEMOPROMETHEUS_EMBEDDING_MODEL", DEFAULT_EMBEDDING_MODEL, env);
73
74
  }
74
75
 
75
76
  export function embeddingDim(env: Env = process.env): number {
76
- const explicit = envInt("PROMETHEUS_MEMORY_EMBEDDING_DIM", NaN, env);
77
+ const explicit = envInt("MNEMOPROMETHEUS_EMBEDDING_DIM", NaN, env);
77
78
  if (Number.isFinite(explicit)) return explicit;
78
79
  return EMBEDDING_DIMS[embeddingModel(env)] ?? 384;
79
80
  }
80
81
 
81
82
  export function embeddingApiKey(env: Env = process.env): string {
82
83
  return envString(
83
- "PROMETHEUS_MEMORY_EMBEDDING_API_KEY",
84
- envString("OPENROUTER_API_KEY", envString("OPENAI_API_KEY", "", env), env),
84
+ "MNEMOPROMETHEUS_EMBEDDING_APROMETHEUS_KEY",
85
+ envString("OPENROUTER_APROMETHEUS_KEY", envString("OPENAI_APROMETHEUS_KEY", "", env), env),
85
86
  env,
86
87
  );
87
88
  }
88
89
 
89
90
  export function embeddingApiUrl(env: Env = process.env): string {
90
91
  return envString(
91
- "PROMETHEUS_MEMORY_EMBEDDING_API_URL",
92
- envString("OPENROUTER_BASE_URL", DEFAULT_EMBEDDING_API_URL, env),
92
+ "MNEMOPROMETHEUS_EMBEDDING_APROMETHEUS_URL",
93
+ envString("OPENROUTER_BASE_URL", DEFAULT_EMBEDDING_APROMETHEUS_URL, env),
93
94
  env,
94
95
  );
95
96
  }
96
97
 
97
98
  export function embeddingsViaApi(env: Env = process.env): boolean {
98
- return envTruthy("PROMETHEUS_MEMORY_EMBEDDINGS_VIA_API", env);
99
+ return envTruthy("MNEMOPROMETHEUS_EMBEDDINGS_VIA_API", env);
99
100
  }
100
101
 
101
102
  export function embeddingsDisabled(env: Env = process.env): boolean {
102
- return envString("PROMETHEUS_MEMORY_NO_EMBEDDINGS", "", env) !== "";
103
+ return envString("MNEMOPROMETHEUS_NO_EMBEDDINGS", "", env) !== "";
103
104
  }
104
105
 
105
106
  export function isApiEmbeddingModel(model = embeddingModel(), env: Env = process.env): boolean {
106
107
  if (model.startsWith("openai/") || model.includes("text-embedding") || model.startsWith("text-embedding"))
107
108
  return true;
108
- const baseUrl = envString("PROMETHEUS_MEMORY_EMBEDDING_API_URL", envString("OPENROUTER_BASE_URL", "", env), env);
109
- if (baseUrl && !baseUrl.includes("openrouter.ai")) return true;
109
+ const baseUrl = envString(
110
+ "MNEMOPROMETHEUS_EMBEDDING_APROMETHEUS_URL",
111
+ envString("OPENROUTER_BASE_URL", "", env),
112
+ env,
113
+ );
114
+ if (baseUrl && !hostMatchesUrl(baseUrl, "openrouter")) return true;
110
115
  return embeddingsViaApi(env);
111
116
  }
112
117
 
113
118
  export function apiEmbeddingsAvailable(env: Env = process.env): boolean {
114
119
  if (embeddingsDisabled(env)) return false;
115
120
  if (!isApiEmbeddingModel(embeddingModel(env), env)) return false;
116
- const baseUrl = envString("PROMETHEUS_MEMORY_EMBEDDING_API_URL", envString("OPENROUTER_BASE_URL", "", env), env);
117
- return Boolean(baseUrl && !baseUrl.includes("openrouter.ai")) || Boolean(embeddingApiKey(env));
121
+ const baseUrl = envString(
122
+ "MNEMOPROMETHEUS_EMBEDDING_APROMETHEUS_URL",
123
+ envString("OPENROUTER_BASE_URL", "", env),
124
+ env,
125
+ );
126
+ return Boolean(baseUrl && !hostMatchesUrl(baseUrl, "openrouter")) || Boolean(embeddingApiKey(env));
118
127
  }
119
128
 
120
129
  export function workingMemoryMaxItems(env: Env = process.env): number {
121
- return envInt("PROMETHEUS_MEMORY_WM_MAX_ITEMS", 10000, env);
130
+ return envInt("MNEMOPROMETHEUS_WM_MAX_ITEMS", 10000, env);
122
131
  }
123
132
 
124
133
  export function workingMemoryTtlHours(env: Env = process.env): number {
125
- return envInt("PROMETHEUS_MEMORY_WM_TTL_HOURS", 24, env);
134
+ return envInt("MNEMOPROMETHEUS_WM_TTL_HOURS", 24, env);
126
135
  }
127
136
 
128
137
  export function episodicRecallLimit(env: Env = process.env): number {
129
- return envInt("PROMETHEUS_MEMORY_EP_LIMIT", 50000, env);
138
+ return envInt("MNEMOPROMETHEUS_EP_LIMIT", 50000, env);
130
139
  }
131
140
 
132
141
  export function sleepBatchSize(env: Env = process.env): number {
133
- return envInt("PROMETHEUS_MEMORY_SLEEP_BATCH", 5000, env);
142
+ return envInt("MNEMOPROMETHEUS_SLEEP_BATCH", 5000, env);
134
143
  }
135
144
 
136
145
  export function scratchpadMaxItems(env: Env = process.env): number {
137
- return envInt("PROMETHEUS_MEMORY_SP_MAX", 1000, env);
146
+ return envInt("MNEMOPROMETHEUS_SP_MAX", 1000, env);
138
147
  }
139
148
 
140
149
  export function recencyHalflifeHours(env: Env = process.env): number {
141
- return envFloat("PROMETHEUS_MEMORY_RECENCY_HALFLIFE", 168, env);
150
+ return envFloat("MNEMOPROMETHEUS_RECENCY_HALFLIFE", 168, env);
142
151
  }
143
152
 
144
153
  export function tier2Days(env: Env = process.env): number {
145
- return envInt("PROMETHEUS_MEMORY_TIER2_DAYS", 30, env);
154
+ return envInt("MNEMOPROMETHEUS_TIER2_DAYS", 30, env);
146
155
  }
147
156
 
148
157
  export function tier3Days(env: Env = process.env): number {
149
- return envInt("PROMETHEUS_MEMORY_TIER3_DAYS", 180, env);
158
+ return envInt("MNEMOPROMETHEUS_TIER3_DAYS", 180, env);
150
159
  }
151
160
 
152
161
  export function tier1Weight(env: Env = process.env): number {
153
- return envFloat("PROMETHEUS_MEMORY_TIER1_WEIGHT", 1.0, env);
162
+ return envFloat("MNEMOPROMETHEUS_TIER1_WEIGHT", 1.0, env);
154
163
  }
155
164
 
156
165
  export function tier2Weight(env: Env = process.env): number {
157
- return envFloat("PROMETHEUS_MEMORY_TIER2_WEIGHT", 0.5, env);
166
+ return envFloat("MNEMOPROMETHEUS_TIER2_WEIGHT", 0.5, env);
158
167
  }
159
168
 
160
169
  export function tier3Weight(env: Env = process.env): number {
161
- return envFloat("PROMETHEUS_MEMORY_TIER3_WEIGHT", 0.25, env);
170
+ return envFloat("MNEMOPROMETHEUS_TIER3_WEIGHT", 0.25, env);
162
171
  }
163
172
 
164
173
  export function degradeBatchSize(env: Env = process.env): number {
165
- return envInt("PROMETHEUS_MEMORY_DEGRADE_BATCH", 100, env);
174
+ return envInt("MNEMOPROMETHEUS_DEGRADE_BATCH", 100, env);
166
175
  }
167
176
 
168
177
  export function smartCompressEnabled(env: Env = process.env): boolean {
169
- return !envDisabled("PROMETHEUS_MEMORY_SMART_COMPRESS", env);
178
+ return !envDisabled("MNEMOPROMETHEUS_SMART_COMPRESS", env);
170
179
  }
171
180
 
172
181
  export function tier3MaxChars(env: Env = process.env): number {
173
- return envInt("PROMETHEUS_MEMORY_TIER3_MAX_CHARS", 300, env);
182
+ return envInt("MNEMOPROMETHEUS_TIER3_MAX_CHARS", 300, env);
174
183
  }
175
184
 
176
185
  export function statedWeight(env: Env = process.env): number {
177
- return envFloat("PROMETHEUS_MEMORY_STATED_WEIGHT", VERACITY_WEIGHT_DEFAULTS.stated, env);
186
+ return envFloat("MNEMOPROMETHEUS_STATED_WEIGHT", VERACITY_WEIGHT_DEFAULTS.stated, env);
178
187
  }
179
188
 
180
189
  export function inferredWeight(env: Env = process.env): number {
181
- return envFloat("PROMETHEUS_MEMORY_INFERRED_WEIGHT", VERACITY_WEIGHT_DEFAULTS.inferred, env);
190
+ return envFloat("MNEMOPROMETHEUS_INFERRED_WEIGHT", VERACITY_WEIGHT_DEFAULTS.inferred, env);
182
191
  }
183
192
 
184
193
  export function toolWeight(env: Env = process.env): number {
185
- return envFloat("PROMETHEUS_MEMORY_TOOL_WEIGHT", VERACITY_WEIGHT_DEFAULTS.tool, env);
194
+ return envFloat("MNEMOPROMETHEUS_TOOL_WEIGHT", VERACITY_WEIGHT_DEFAULTS.tool, env);
186
195
  }
187
196
 
188
197
  export function importedWeight(env: Env = process.env): number {
189
- return envFloat("PROMETHEUS_MEMORY_IMPORTED_WEIGHT", VERACITY_WEIGHT_DEFAULTS.imported, env);
198
+ return envFloat("MNEMOPROMETHEUS_IMPORTED_WEIGHT", VERACITY_WEIGHT_DEFAULTS.imported, env);
190
199
  }
191
200
 
192
201
  export function unknownWeight(env: Env = process.env): number {
193
- return envFloat("PROMETHEUS_MEMORY_UNKNOWN_WEIGHT", VERACITY_WEIGHT_DEFAULTS.unknown, env);
202
+ return envFloat("MNEMOPROMETHEUS_UNKNOWN_WEIGHT", VERACITY_WEIGHT_DEFAULTS.unknown, env);
194
203
  }
195
204
 
196
205
  export function veracityWeightOverrides(env: Env = process.env): string[] {
197
206
  const names = [
198
- "PROMETHEUS_MEMORY_STATED_WEIGHT",
199
- "PROMETHEUS_MEMORY_INFERRED_WEIGHT",
200
- "PROMETHEUS_MEMORY_TOOL_WEIGHT",
201
- "PROMETHEUS_MEMORY_IMPORTED_WEIGHT",
202
- "PROMETHEUS_MEMORY_UNKNOWN_WEIGHT",
207
+ "MNEMOPROMETHEUS_STATED_WEIGHT",
208
+ "MNEMOPROMETHEUS_INFERRED_WEIGHT",
209
+ "MNEMOPROMETHEUS_TOOL_WEIGHT",
210
+ "MNEMOPROMETHEUS_IMPORTED_WEIGHT",
211
+ "MNEMOPROMETHEUS_UNKNOWN_WEIGHT",
203
212
  ];
204
213
  const overrides: string[] = [];
205
214
  for (const name of names) {
@@ -209,19 +218,19 @@ export function veracityWeightOverrides(env: Env = process.env): string[] {
209
218
  }
210
219
 
211
220
  export function vecType(env: Env = process.env): VecType {
212
- return envOneOf("PROMETHEUS_MEMORY_VEC_TYPE", ["float32", "int8", "bit"] as const, "int8", env);
221
+ return envOneOf("MNEMOPROMETHEUS_VEC_TYPE", ["float32", "int8", "bit"] as const, "int8", env);
213
222
  }
214
223
 
215
224
  export function vectorWeight(env: Env = process.env): number {
216
- return envFloat("PROMETHEUS_MEMORY_VEC_WEIGHT", 0.5, env);
225
+ return envFloat("MNEMOPROMETHEUS_VEC_WEIGHT", 0.5, env);
217
226
  }
218
227
 
219
228
  export function ftsWeight(env: Env = process.env): number {
220
- return envFloat("PROMETHEUS_MEMORY_FTS_WEIGHT", 0.3, env);
229
+ return envFloat("MNEMOPROMETHEUS_FTS_WEIGHT", 0.3, env);
221
230
  }
222
231
 
223
232
  export function importanceWeight(env: Env = process.env): number {
224
- return envFloat("PROMETHEUS_MEMORY_IMPORTANCE_WEIGHT", 0.2, env);
233
+ return envFloat("MNEMOPROMETHEUS_IMPORTANCE_WEIGHT", 0.2, env);
225
234
  }
226
235
 
227
236
  export function normalizedRecallWeights(
@@ -244,83 +253,104 @@ export function normalizedRecallWeights(
244
253
  }
245
254
 
246
255
  export function autoMigrateEnabled(env: Env = process.env): boolean {
247
- return envString("PROMETHEUS_MEMORY_AUTO_MIGRATE", "1", env) !== "0";
256
+ return envString("MNEMOPROMETHEUS_AUTO_MIGRATE", "1", env) !== "0";
248
257
  }
249
258
 
250
259
  export function proactiveLinkingEnabled(env: Env = process.env): boolean {
251
- return envString("PROMETHEUS_MEMORY_PROACTIVE_LINKING", "0", env) === "1";
260
+ return envString("MNEMOPROMETHEUS_PROACTIVE_LINKING", "0", env) === "1";
261
+ }
262
+
263
+ export interface RecallFeatureFlags {
264
+ polyphonicRecall?: boolean;
265
+ enhancedRecall?: boolean;
266
+ }
267
+
268
+ let polyphonicRecallDefault = false;
269
+ let enhancedRecallDefault = false;
270
+
271
+ /**
272
+ * Sets process-wide defaults for the env-gated recall features. Host configuration
273
+ * (e.g. the coding-agent `mnemopi.polyphonicRecall` / `mnemopi.enhancedRecall`
274
+ * settings) lands here; the `MNEMOPROMETHEUS_POLYPHONIC_RECALL` / `MNEMOPROMETHEUS_ENHANCED_RECALL`
275
+ * environment variables still win whenever they are set.
276
+ */
277
+ export function configureRecallFeatures(flags: RecallFeatureFlags): void {
278
+ if (flags.polyphonicRecall !== undefined) polyphonicRecallDefault = flags.polyphonicRecall;
279
+ if (flags.enhancedRecall !== undefined) enhancedRecallDefault = flags.enhancedRecall;
252
280
  }
253
281
 
254
282
  export function polyphonicRecallEnabled(env: Env = process.env): boolean {
255
- return envString("PROMETHEUS_MEMORY_POLYPHONIC_RECALL", "0", env) === "1";
283
+ const value = envOptionalString("MNEMOPROMETHEUS_POLYPHONIC_RECALL", env);
284
+ return value === undefined ? polyphonicRecallDefault : value === "1";
256
285
  }
257
286
 
258
287
  export function temporalHalflifeHours(env: Env = process.env): number {
259
- return envFloat("PROMETHEUS_MEMORY_TEMPORAL_HALFLIFE_HOURS", 24, env);
288
+ return envFloat("MNEMOPROMETHEUS_TEMPORAL_HALFLIFE_HOURS", 24, env);
260
289
  }
261
290
 
262
291
  export function enhancedRecallEnabled(env: Env = process.env): boolean {
263
- return envString("PROMETHEUS_MEMORY_ENHANCED_RECALL", "0", env) === "1";
292
+ const value = envOptionalString("MNEMOPROMETHEUS_ENHANCED_RECALL", env);
293
+ return value === undefined ? enhancedRecallDefault : value === "1";
264
294
  }
265
295
 
266
296
  export function llmEnabled(env: Env = process.env): boolean {
267
- return envBool("PROMETHEUS_MEMORY_LLM_ENABLED", true, env);
297
+ return envBool("MNEMOPROMETHEUS_LLM_ENABLED", true, env);
268
298
  }
269
299
 
270
300
  export function llmMaxTokens(env: Env = process.env): number {
271
- return envInt("PROMETHEUS_MEMORY_LLM_MAX_TOKENS", 2048, env);
301
+ return envInt("MNEMOPROMETHEUS_LLM_MAX_TOKENS", 2048, env);
272
302
  }
273
303
 
274
304
  export function llmThreads(env: Env = process.env): number {
275
- return envInt("PROMETHEUS_MEMORY_LLM_N_THREADS", 4, env);
305
+ return envInt("MNEMOPROMETHEUS_LLM_N_THREADS", 4, env);
276
306
  }
277
307
 
278
308
  export function llmContext(env: Env = process.env): number {
279
- return envInt("PROMETHEUS_MEMORY_LLM_N_CTX", 2048, env);
309
+ return envInt("MNEMOPROMETHEUS_LLM_N_CTX", 2048, env);
280
310
  }
281
311
 
282
312
  export function llmRepo(env: Env = process.env): string {
283
- return envString("PROMETHEUS_MEMORY_LLM_REPO", DEFAULT_LLM_MODEL_REPO, env);
313
+ return envString("MNEMOPROMETHEUS_LLM_REPO", DEFAULT_LLM_MODEL_REPO, env);
284
314
  }
285
315
 
286
316
  export function llmFile(env: Env = process.env): string {
287
- return envString("PROMETHEUS_MEMORY_LLM_FILE", DEFAULT_LLM_MODEL_FILE, env);
317
+ return envString("MNEMOPROMETHEUS_LLM_FILE", DEFAULT_LLM_MODEL_FILE, env);
288
318
  }
289
319
 
290
320
  export function llmModelFiles(env: Env = process.env): readonly [repo: string, file: string] {
291
- const repo = envOptionalString("PROMETHEUS_MEMORY_LLM_REPO", env);
292
- const file = envOptionalString("PROMETHEUS_MEMORY_LLM_FILE", env);
321
+ const repo = envOptionalString("MNEMOPROMETHEUS_LLM_REPO", env);
322
+ const file = envOptionalString("MNEMOPROMETHEUS_LLM_FILE", env);
293
323
  return repo && file ? [repo, file] : [DEFAULT_LLM_MODEL_REPO, DEFAULT_LLM_MODEL_FILE];
294
324
  }
295
325
 
296
326
  export function llmBaseUrl(env: Env = process.env): string {
297
- return envString("PROMETHEUS_MEMORY_LLM_BASE_URL", "", env).replace(/\/+$/, "");
327
+ return envString("MNEMOPROMETHEUS_LLM_BASE_URL", "", env).replace(/\/+$/, "");
298
328
  }
299
329
 
300
330
  export function llmApiKey(env: Env = process.env): string {
301
- return envString("PROMETHEUS_MEMORY_LLM_API_KEY", "", env);
331
+ return envString("MNEMOPROMETHEUS_LLM_APROMETHEUS_KEY", "", env);
302
332
  }
303
333
 
304
334
  export function llmModel(env: Env = process.env): string {
305
- return envString("PROMETHEUS_MEMORY_LLM_MODEL", "", env);
335
+ return envString("MNEMOPROMETHEUS_LLM_MODEL", "", env);
306
336
  }
307
337
 
308
338
  export function hostLlmEnabled(env: Env = process.env): boolean {
309
- return envBool("PROMETHEUS_MEMORY_HOST_LLM_ENABLED", false, env);
339
+ return envBool("MNEMOPROMETHEUS_HOST_LLM_ENABLED", false, env);
310
340
  }
311
341
 
312
342
  export function hostLlmProvider(env: Env = process.env): string | undefined {
313
- return envOptionalString("PROMETHEUS_MEMORY_HOST_LLM_PROVIDER", env);
343
+ return envOptionalString("MNEMOPROMETHEUS_HOST_LLM_PROVIDER", env);
314
344
  }
315
345
 
316
346
  export function hostLlmModel(env: Env = process.env): string | undefined {
317
- return envOptionalString("PROMETHEUS_MEMORY_HOST_LLM_MODEL", env);
347
+ return envOptionalString("MNEMOPROMETHEUS_HOST_LLM_MODEL", env);
318
348
  }
319
349
 
320
350
  export function hostLlmContext(env: Env = process.env): number {
321
- return envInt("PROMETHEUS_MEMORY_HOST_LLM_N_CTX", 32000, env);
351
+ return envInt("MNEMOPROMETHEUS_HOST_LLM_N_CTX", 32000, env);
322
352
  }
323
353
 
324
354
  export function sleepPrompt(env: Env = process.env): string {
325
- return envString("PROMETHEUS_MEMORY_SLEEP_PROMPT", "", env).trim();
355
+ return envString("MNEMOPROMETHEUS_SLEEP_PROMPT", "", env).trim();
326
356
  }
package/src/core/banks.ts CHANGED
@@ -4,9 +4,9 @@ import { join } from "node:path";
4
4
  import { dataDir as configuredDataDir } from "../config";
5
5
  import { closeQuietly, openDatabase } from "../db";
6
6
 
7
- export const DEFAULT_DATA_DIR = join(homedir(), ".prometheus", "memory", "data");
7
+ export const DEFAULT_DATA_DIR = join(homedir(), ".hermes", "mnemopi", "data");
8
8
  export const BANKS_DIR = join(DEFAULT_DATA_DIR, "banks");
9
- const DB_FILENAME = "prometheus-memory.db";
9
+ const DB_FILENAME = "mnemopi.db";
10
10
 
11
11
  export class ValueError extends Error {
12
12
  override name = "ValueError";
@@ -1,6 +1,7 @@
1
1
  import type { SQLQueryBindings } from "bun:sqlite";
2
2
  import { generateId, stableMemoryId } from "../../util/ids";
3
3
  import { aaakEncode } from "../aaak";
4
+ import { EpisodicGraph } from "../episodic-graph";
4
5
  import { heuristicExtractFacts } from "../extraction";
5
6
  import { clampVeracity } from "../veracity-consolidation";
6
7
  import { scheduleEmbedding } from "./helpers";
@@ -51,11 +52,11 @@ function envInt(name: string, defaultValue: number): number {
51
52
  return Number.isFinite(parsed) ? parsed : defaultValue;
52
53
  }
53
54
 
54
- const SLEEP_BATCH_SIZE = envInt("PROMETHEUS_MEMORY_SLEEP_BATCH", 5000);
55
- const TIER2_DAYS = envInt("PROMETHEUS_MEMORY_TIER2_DAYS", 30);
56
- const TIER3_DAYS = envInt("PROMETHEUS_MEMORY_TIER3_DAYS", 180);
57
- const DEGRADE_BATCH_SIZE = envInt("PROMETHEUS_MEMORY_DEGRADE_BATCH", 100);
58
- const TIER3_MAX_CHARS = envInt("PROMETHEUS_MEMORY_TIER3_MAX_CHARS", 300);
55
+ const SLEEP_BATCH_SIZE = envInt("MNEMOPROMETHEUS_SLEEP_BATCH", 5000);
56
+ const TIER2_DAYS = envInt("MNEMOPROMETHEUS_TIER2_DAYS", 30);
57
+ const TIER3_DAYS = envInt("MNEMOPROMETHEUS_TIER3_DAYS", 180);
58
+ const DEGRADE_BATCH_SIZE = envInt("MNEMOPROMETHEUS_DEGRADE_BATCH", 100);
59
+ const TIER3_MAX_CHARS = envInt("MNEMOPROMETHEUS_TIER3_MAX_CHARS", 300);
59
60
 
60
61
  function isoNow(): string {
61
62
  return new Date().toISOString();
@@ -261,6 +262,33 @@ function insertKg(
261
262
  });
262
263
  }
263
264
 
265
+ /**
266
+ * Populate the episodic graph (gists, facts, edges) for a freshly consolidated
267
+ * memory. The graph backs Polyphonic Recall's `graph` voice, which relies on
268
+ * `findGistsByParticipant`, `findFactsBySubject`, and `findRelatedMemories`.
269
+ *
270
+ * Best-effort: failures (closed DB, missing optional tables) MUST NOT roll back
271
+ * the consolidation that already wrote the episodic row, so any throw here is
272
+ * swallowed. Unlike the proactive-link path in `store.ts` this is unconditional
273
+ * — consolidation is the explicit "settle and compress" step where the graph
274
+ * is supposed to gain edges.
275
+ */
276
+ function ingestIntoEpisodicGraph(beam: BeamMemoryState, memoryId: string, summary: string): void {
277
+ try {
278
+ const graph =
279
+ beam.episodicGraph instanceof EpisodicGraph
280
+ ? beam.episodicGraph
281
+ : new EpisodicGraph({ db: beam.db, dbPath: beam.dbPath });
282
+ graph.ingestMemory(summary, memoryId, {
283
+ sessionId: sourceSession(beam),
284
+ linkExisting: true,
285
+ extractEntities: true,
286
+ });
287
+ } catch {
288
+ // Graph enrichment is best-effort and never blocks consolidation.
289
+ }
290
+ }
291
+
264
292
  export function consolidateToEpisodic(
265
293
  beam: BeamMemoryState,
266
294
  summary: string,
@@ -299,6 +327,7 @@ export function consolidateToEpisodic(
299
327
  ],
300
328
  );
301
329
  extractAndStoreFacts(beam, summary, 0, memoryId);
330
+ ingestIntoEpisodicGraph(beam, memoryId, summary);
302
331
  scheduleEmbedding(beam, [{ memoryId, content: summary }]);
303
332
  emitEvent(beam, "MEMORY_CONSOLIDATED", memoryId, summary, source, importance, {
304
333
  summary_of: [...sourceWmIds],
@@ -1,8 +1,9 @@
1
1
  import type { Database } from "bun:sqlite";
2
+ import { logger } from "@prometheus-ai/utils";
2
3
  import { generateId as generateTimedId, sha256Hex16, stableMemoryId } from "../../util/ids";
3
4
  import { currentEmbeddingModel, embed } from "../embeddings";
4
- import { getMnemopiRuntimeOptions, withMnemopiRuntimeOptions } from "../runtime-options";
5
- import { cosineSimilarity as vectorCosineSimilarity } from "../vector-math";
5
+ import { getMnemopiRuntimeOptions, mnemopiDebugEnabled, withMnemopiRuntimeOptions } from "../runtime-options";
6
+ import { buildExactVectorIndex, searchExactVectorIndex } from "../vector-index";
6
7
  import type { BeamMemoryState, JsonValue, Metadata } from "./types";
7
8
 
8
9
  export type Vector = number[];
@@ -162,9 +163,9 @@ export function normalizeWeights(
162
163
  ftsWeight: number | null | undefined,
163
164
  importanceWeight: number | null | undefined,
164
165
  ): HybridWeights {
165
- let vw = Math.max(0, vecWeight ?? envNumber("PROMETHEUS_MEMORY_VEC_WEIGHT", DEFAULT_WEIGHTS[0]));
166
- let fw = Math.max(0, ftsWeight ?? envNumber("PROMETHEUS_MEMORY_FTS_WEIGHT", DEFAULT_WEIGHTS[1]));
167
- let iw = Math.max(0, importanceWeight ?? envNumber("PROMETHEUS_MEMORY_IMPORTANCE_WEIGHT", DEFAULT_WEIGHTS[2]));
166
+ let vw = Math.max(0, vecWeight ?? envNumber("MNEMOPROMETHEUS_VEC_WEIGHT", DEFAULT_WEIGHTS[0]));
167
+ let fw = Math.max(0, ftsWeight ?? envNumber("MNEMOPROMETHEUS_FTS_WEIGHT", DEFAULT_WEIGHTS[1]));
168
+ let iw = Math.max(0, importanceWeight ?? envNumber("MNEMOPROMETHEUS_IMPORTANCE_WEIGHT", DEFAULT_WEIGHTS[2]));
168
169
  if (!Number.isFinite(vw)) vw = 0;
169
170
  if (!Number.isFinite(fw)) fw = 0;
170
171
  if (!Number.isFinite(iw)) iw = 0;
@@ -551,16 +552,10 @@ export function inMemoryVecSearch(db: Database, queryEmbedding: readonly number[
551
552
  LIMIT 10000
552
553
  `)
553
554
  .all() as Record<string, unknown>[];
554
- const results: VectorDistanceResult[] = [];
555
- for (const row of rows) {
556
- const vec = decodeVector(String(row.embedding_json ?? ""));
557
- if (vec === null) continue;
558
- const sim = vectorCosineSimilarity(queryEmbedding, vec);
559
- if (sim === 0 && (queryEmbedding.every(n => n === 0) || vec.every(n => n === 0))) continue;
560
- results.push({ rowid: Number(row.rowid), distance: 1 - sim });
561
- }
562
- results.sort((a, b) => a.distance - b.distance || a.rowid - b.rowid);
563
- return results.slice(0, Math.max(0, Math.trunc(k)));
555
+ const index = buildExactVectorIndex(
556
+ rows.map(row => ({ id: Number(row.rowid), vector: decodeVector(String(row.embedding_json ?? "")) })),
557
+ );
558
+ return searchExactVectorIndex(index, queryEmbedding, k).map(hit => ({ rowid: hit.id, distance: 1 - hit.score }));
564
559
  } catch {
565
560
  return [];
566
561
  }
@@ -574,7 +569,7 @@ export function workingMemoryVecSearch(
574
569
  ): WorkingVectorResult[] {
575
570
  if (queryEmbedding.length === 0) return [];
576
571
  try {
577
- const limit = process.env.PROMETHEUS_MEMORY_BEAM_MODE ? 500_000 : 50_000;
572
+ const limit = process.env.MNEMOPROMETHEUS_BEAM_MODE ? 500_000 : 50_000;
578
573
  const rows = db
579
574
  .query(`
580
575
  SELECT wm.id, me.embedding_json
@@ -585,16 +580,10 @@ export function workingMemoryVecSearch(
585
580
  LIMIT ?
586
581
  `)
587
582
  .all(now.toISOString(), limit) as Record<string, unknown>[];
588
- const results: WorkingVectorResult[] = [];
589
- for (const row of rows) {
590
- const vec = decodeVector(String(row.embedding_json ?? ""));
591
- if (vec === null) continue;
592
- const sim = vectorCosineSimilarity(queryEmbedding, vec);
593
- if (sim === 0 && (queryEmbedding.every(n => n === 0) || vec.every(n => n === 0))) continue;
594
- results.push({ id: String(row.id), sim });
595
- }
596
- results.sort((a, b) => b.sim - a.sim || a.id.localeCompare(b.id));
597
- return results.slice(0, Math.max(0, Math.trunc(k)));
583
+ const index = buildExactVectorIndex(
584
+ rows.map(row => ({ id: String(row.id), vector: decodeVector(String(row.embedding_json ?? "")) })),
585
+ );
586
+ return searchExactVectorIndex(index, queryEmbedding, k).map(hit => ({ id: hit.id, sim: hit.score }));
598
587
  } catch {
599
588
  return [];
600
589
  }
@@ -944,12 +933,16 @@ async function runEmbedding(beam: BeamMemoryState, items: readonly EmbedItem[]):
944
933
  }
945
934
  });
946
935
  insertMany(items);
947
- } catch {
936
+ } catch (error) {
948
937
  // Background embedding generation is best-effort: a failing provider, a closed DB
949
938
  // during shutdown, or a transient API error must never disrupt the synchronous
950
939
  // remember()/consolidate() that scheduled it. Production recall silently degrades
951
940
  // to FTS-only for the affected rows, which is the same shape as a misconfigured
952
- // provider.
941
+ // provider. Log so the failure is diagnosable (#2322).
942
+ logger[mnemopiDebugEnabled() ? "warn" : "debug"]("mnemopi: background embedding failed", {
943
+ itemCount: items.length,
944
+ error: String(error),
945
+ });
953
946
  }
954
947
  }
955
948
 
@@ -87,14 +87,14 @@ function normalizeConfig(options: BeamMemoryOptions): BeamConfig {
87
87
  function autoMigrateAnnotations(db: Database, dbPath: string | undefined): void {
88
88
  if (dbPath === undefined || dbPath === ":memory:" || !existsSync(dbPath)) return;
89
89
  if (!hasPendingMigration(db)) return;
90
- if (process.env.PROMETHEUS_MEMORY_AUTO_MIGRATE === "0") {
90
+ if (process.env.MNEMOPROMETHEUS_AUTO_MIGRATE === "0") {
91
91
  const row = db
92
92
  .query(
93
93
  "SELECT COUNT(*) AS count FROM triples WHERE predicate IN ('mentions', 'fact', 'occurred_on', 'has_source')",
94
94
  )
95
95
  .get() as { count: number };
96
96
  console.warn(
97
- `PROMETHEUS_MEMORY_AUTO_MIGRATE=0: ${row.count} annotation rows pending; run scripts/migrate_triplestore_split.py manually.`,
97
+ `MNEMOPROMETHEUS_AUTO_MIGRATE=0: ${row.count} annotation rows pending; run scripts/migrate_triplestore_split.py manually.`,
98
98
  );
99
99
  return;
100
100
  }