@prometheus-ai/memory 0.5.0

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 (128) hide show
  1. package/README.md +107 -0
  2. package/dist/types/cli.d.ts +35 -0
  3. package/dist/types/config.d.ts +77 -0
  4. package/dist/types/core/aaak.d.ts +55 -0
  5. package/dist/types/core/annotations.d.ts +75 -0
  6. package/dist/types/core/banks.d.ts +33 -0
  7. package/dist/types/core/beam/consolidate.d.ts +32 -0
  8. package/dist/types/core/beam/helpers.d.ts +76 -0
  9. package/dist/types/core/beam/index.d.ts +59 -0
  10. package/dist/types/core/beam/recall.d.ts +32 -0
  11. package/dist/types/core/beam/schema.d.ts +2 -0
  12. package/dist/types/core/beam/store.d.ts +35 -0
  13. package/dist/types/core/beam/types.d.ts +233 -0
  14. package/dist/types/core/binary-vectors.d.ts +54 -0
  15. package/dist/types/core/chat-normalize.d.ts +13 -0
  16. package/dist/types/core/content-sanitizer.d.ts +18 -0
  17. package/dist/types/core/cost-log.d.ts +13 -0
  18. package/dist/types/core/embeddings.d.ts +44 -0
  19. package/dist/types/core/entities.d.ts +7 -0
  20. package/dist/types/core/episodic-graph.d.ts +89 -0
  21. package/dist/types/core/extraction/client.d.ts +31 -0
  22. package/dist/types/core/extraction/diagnostics.d.ts +51 -0
  23. package/dist/types/core/extraction/prompts.d.ts +2 -0
  24. package/dist/types/core/extraction.d.ts +6 -0
  25. package/dist/types/core/index.d.ts +4 -0
  26. package/dist/types/core/llm-backends.d.ts +21 -0
  27. package/dist/types/core/local-llm.d.ts +15 -0
  28. package/dist/types/core/memory.d.ts +160 -0
  29. package/dist/types/core/migrations/e6-triplestore-split.d.ts +17 -0
  30. package/dist/types/core/migrations/index.d.ts +1 -0
  31. package/dist/types/core/mmr.d.ts +8 -0
  32. package/dist/types/core/orchestrator.d.ts +20 -0
  33. package/dist/types/core/patterns.d.ts +61 -0
  34. package/dist/types/core/plugins.d.ts +109 -0
  35. package/dist/types/core/polyphonic-recall.d.ts +66 -0
  36. package/dist/types/core/query-cache.d.ts +46 -0
  37. package/dist/types/core/query-intent.d.ts +20 -0
  38. package/dist/types/core/recall-diagnostics.d.ts +48 -0
  39. package/dist/types/core/runtime-options.d.ts +68 -0
  40. package/dist/types/core/shmr.d.ts +56 -0
  41. package/dist/types/core/streaming.d.ts +136 -0
  42. package/dist/types/core/synonyms.d.ts +46 -0
  43. package/dist/types/core/temporal-parser.d.ts +16 -0
  44. package/dist/types/core/token-counter.d.ts +8 -0
  45. package/dist/types/core/triples.d.ts +63 -0
  46. package/dist/types/core/typed-memory.d.ts +39 -0
  47. package/dist/types/core/vector-math.d.ts +1 -0
  48. package/dist/types/core/veracity-consolidation.d.ts +60 -0
  49. package/dist/types/core/weibull.d.ts +96 -0
  50. package/dist/types/db.d.ts +16 -0
  51. package/dist/types/diagnose.d.ts +24 -0
  52. package/dist/types/dr/index.d.ts +1 -0
  53. package/dist/types/dr/recovery.d.ts +68 -0
  54. package/dist/types/index.d.ts +5 -0
  55. package/dist/types/mcp-server.d.ts +40 -0
  56. package/dist/types/mcp-tools.d.ts +484 -0
  57. package/dist/types/migrations/e6-triplestore-split.d.ts +1 -0
  58. package/dist/types/migrations/index.d.ts +1 -0
  59. package/dist/types/types.d.ts +145 -0
  60. package/dist/types/util/datetime.d.ts +8 -0
  61. package/dist/types/util/env.d.ts +10 -0
  62. package/dist/types/util/ids.d.ts +3 -0
  63. package/dist/types/util/lru.d.ts +12 -0
  64. package/dist/types/util/regex.d.ts +10 -0
  65. package/package.json +85 -0
  66. package/src/cli.ts +398 -0
  67. package/src/config.ts +326 -0
  68. package/src/core/aaak.ts +142 -0
  69. package/src/core/annotations.ts +457 -0
  70. package/src/core/banks.ts +133 -0
  71. package/src/core/beam/consolidate.ts +965 -0
  72. package/src/core/beam/helpers.ts +977 -0
  73. package/src/core/beam/index.ts +353 -0
  74. package/src/core/beam/recall.ts +1100 -0
  75. package/src/core/beam/schema.ts +423 -0
  76. package/src/core/beam/store.ts +829 -0
  77. package/src/core/beam/types.ts +268 -0
  78. package/src/core/binary-vectors.ts +317 -0
  79. package/src/core/chat-normalize.ts +160 -0
  80. package/src/core/content-sanitizer.ts +136 -0
  81. package/src/core/cost-log.ts +103 -0
  82. package/src/core/embeddings.ts +423 -0
  83. package/src/core/entities.ts +259 -0
  84. package/src/core/episodic-graph.ts +708 -0
  85. package/src/core/extraction/client.ts +162 -0
  86. package/src/core/extraction/diagnostics.ts +193 -0
  87. package/src/core/extraction/prompts.ts +31 -0
  88. package/src/core/extraction.ts +335 -0
  89. package/src/core/index.ts +30 -0
  90. package/src/core/llm-backends.ts +51 -0
  91. package/src/core/local-llm.ts +436 -0
  92. package/src/core/memory.ts +630 -0
  93. package/src/core/migrations/e6-triplestore-split.ts +211 -0
  94. package/src/core/migrations/index.ts +1 -0
  95. package/src/core/mmr.ts +71 -0
  96. package/src/core/orchestrator.ts +62 -0
  97. package/src/core/patterns.ts +484 -0
  98. package/src/core/plugins.ts +375 -0
  99. package/src/core/polyphonic-recall.ts +563 -0
  100. package/src/core/query-cache.ts +354 -0
  101. package/src/core/query-intent.ts +139 -0
  102. package/src/core/recall-diagnostics.ts +157 -0
  103. package/src/core/runtime-options.ts +119 -0
  104. package/src/core/shmr.ts +460 -0
  105. package/src/core/streaming.ts +419 -0
  106. package/src/core/synonyms.ts +197 -0
  107. package/src/core/temporal-parser.ts +363 -0
  108. package/src/core/token-counter.ts +30 -0
  109. package/src/core/triples.ts +454 -0
  110. package/src/core/typed-memory.ts +407 -0
  111. package/src/core/vector-math.ts +23 -0
  112. package/src/core/veracity-consolidation.ts +477 -0
  113. package/src/core/weibull.ts +124 -0
  114. package/src/db.ts +128 -0
  115. package/src/diagnose.ts +174 -0
  116. package/src/dr/index.ts +1 -0
  117. package/src/dr/recovery.ts +405 -0
  118. package/src/index.ts +33 -0
  119. package/src/mcp-server.ts +155 -0
  120. package/src/mcp-tools.ts +970 -0
  121. package/src/migrations/e6-triplestore-split.ts +1 -0
  122. package/src/migrations/index.ts +1 -0
  123. package/src/types.ts +157 -0
  124. package/src/util/datetime.ts +69 -0
  125. package/src/util/env.ts +65 -0
  126. package/src/util/ids.ts +19 -0
  127. package/src/util/lru.ts +48 -0
  128. package/src/util/regex.ts +165 -0
@@ -0,0 +1,160 @@
1
+ type ExtractionRate = {
2
+ total: number;
3
+ survived: number;
4
+ dropped: number;
5
+ rate: number;
6
+ dropped_samples: string[];
7
+ };
8
+
9
+ const CONTRACTIONS: readonly [RegExp, string][] = [
10
+ [/\bu\b/g, "you"],
11
+ [/\bur\b/g, "your"],
12
+ [/\bu're\b/g, "you are"],
13
+ [/\br\b/g, "are"],
14
+ [/\by\b/g, "why"],
15
+ [/\bb4\b/g, "before"],
16
+ [/\bbc\b/g, "because"],
17
+ [/\bcuz\b/g, "because"],
18
+ [/\bgonna\b/g, "going to"],
19
+ [/\bwanna\b/g, "want to"],
20
+ [/\bgotta\b/g, "got to"],
21
+ [/\bkinda\b/g, "kind of"],
22
+ [/\bsorta\b/g, "sort of"],
23
+ [/\bdunno\b/g, "don't know"],
24
+ [/\blemme\b/g, "let me"],
25
+ [/\bgimme\b/g, "give me"],
26
+ [/\boutta\b/g, "out of"],
27
+ [/\bhafta\b/g, "have to"],
28
+ [/\bshoulda\b/g, "should have"],
29
+ [/\bwoulda\b/g, "would have"],
30
+ [/\bcoulda\b/g, "could have"],
31
+ ];
32
+
33
+ const FILLER_WORDS: Readonly<Record<string, true>> = {
34
+ afaik: true,
35
+ brb: true,
36
+ fr: true,
37
+ fwiw: true,
38
+ idc: true,
39
+ idk: true,
40
+ iirc: true,
41
+ ikr: true,
42
+ imho: true,
43
+ imo: true,
44
+ irl: true,
45
+ istg: true,
46
+ lmao: true,
47
+ lmaoo: true,
48
+ lmfao: true,
49
+ lol: true,
50
+ ngl: true,
51
+ nvm: true,
52
+ omg: true,
53
+ omgg: true,
54
+ omggg: true,
55
+ rofl: true,
56
+ smh: true,
57
+ tbh: true,
58
+ tldr: true,
59
+ w: true,
60
+ wdym: true,
61
+ wtf: true,
62
+ };
63
+
64
+ const FRAGMENT_STARTERS: Readonly<Record<string, true>> = {
65
+ building: true,
66
+ checking: true,
67
+ coming: true,
68
+ deploying: true,
69
+ feeling: true,
70
+ fixing: true,
71
+ going: true,
72
+ hoping: true,
73
+ looking: true,
74
+ planning: true,
75
+ running: true,
76
+ testing: true,
77
+ thinking: true,
78
+ trying: true,
79
+ wondering: true,
80
+ working: true,
81
+ };
82
+
83
+ const EDGE_PUNCTUATION_RE = /^[.,!?;:'"]+|[.,!?;:'"]+$/g;
84
+ const REPEATED_CHARS_RE = /(.)\1{2,}/g;
85
+ function replaceNonAsciiRuns(value: string): string {
86
+ let normalized = "";
87
+ let inNonAsciiRun = false;
88
+ for (let index = 0; index < value.length; index++) {
89
+ const char = value[index];
90
+ if (char === undefined) continue;
91
+ if (char.charCodeAt(0) > 0x7f) {
92
+ if (!inNonAsciiRun) normalized += " ";
93
+ inNonAsciiRun = true;
94
+ } else {
95
+ normalized += char;
96
+ inNonAsciiRun = false;
97
+ }
98
+ }
99
+ return normalized;
100
+ }
101
+
102
+ export function normalizeChat(text: string, options: { add_implicit_subjects?: boolean } = {}): string | null {
103
+ const addImplicitSubjects = options.add_implicit_subjects ?? true;
104
+ if (text.trim().length === 0) return null;
105
+
106
+ let normalized = text.toLowerCase().trim();
107
+ for (const [pattern, replacement] of CONTRACTIONS) {
108
+ normalized = normalized.replace(pattern, replacement);
109
+ }
110
+
111
+ const meaningful = normalized
112
+ .split(/\s+/)
113
+ .filter(word => FILLER_WORDS[word.replace(EDGE_PUNCTUATION_RE, "")] !== true);
114
+ if (meaningful.length === 0) return null;
115
+
116
+ normalized = meaningful.join(" ");
117
+ normalized = normalized.replace(REPEATED_CHARS_RE, "$1");
118
+ normalized = replaceNonAsciiRuns(normalized);
119
+ normalized = normalized.split(/\s+/).filter(Boolean).join(" ");
120
+
121
+ const words = normalized.length === 0 ? [] : normalized.split(" ");
122
+ const wordCount = words.length;
123
+ if (wordCount < 2) {
124
+ if (wordCount === 1 && (words[0]?.length ?? 0) > 5) return normalized;
125
+ return null;
126
+ }
127
+
128
+ if (addImplicitSubjects && wordCount === 2) {
129
+ const firstWord = words[0] ?? "";
130
+ if (FRAGMENT_STARTERS[firstWord] === true) normalized = `i am ${normalized}`;
131
+ }
132
+
133
+ return normalized;
134
+ }
135
+ export function normalizeBatch(messages: string[]): (string | null)[] {
136
+ return messages.map(message => normalizeChat(message));
137
+ }
138
+ export function extractionRate(messages: string[]): ExtractionRate {
139
+ const normalized = normalizeBatch(messages);
140
+ let survived = 0;
141
+ const droppedSamples: string[] = [];
142
+
143
+ for (let i = 0; i < messages.length; i += 1) {
144
+ if (normalized[i] !== null) {
145
+ survived += 1;
146
+ } else if (droppedSamples.length < 5) {
147
+ const message = messages[i];
148
+ if (message !== undefined) droppedSamples.push(message);
149
+ }
150
+ }
151
+
152
+ const dropped = messages.length - survived;
153
+ return {
154
+ total: messages.length,
155
+ survived,
156
+ dropped,
157
+ rate: messages.length === 0 ? 0.0 : Math.round((survived / messages.length) * 1000) / 1000,
158
+ dropped_samples: droppedSamples,
159
+ };
160
+ }
@@ -0,0 +1,136 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ export const SIZE_HARD_CAP = 1_000_000;
7
+ export const SIZE_BASE64_CHECK = 100_000;
8
+ export const ENTROPY_THRESHOLD = 5.0;
9
+
10
+ const DATA_URI_RE = /^data:(?<mime>[^;]+)?(?:;base64)?,(?<payload>.*)/i;
11
+ const BASE64_RE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
12
+
13
+ export interface BlobMetadata {
14
+ blob_ref?: string;
15
+ original_size?: number;
16
+ mime?: string;
17
+ extraction_reason?: "data_uri" | "size_cap" | "high_entropy";
18
+ entropy?: number;
19
+ }
20
+
21
+ export function blobRoot(env: NodeJS.ProcessEnv = process.env): string {
22
+ return env.PROMETHEUS_MEMORY_BLOB_DIR && env.PROMETHEUS_MEMORY_BLOB_DIR.length > 0
23
+ ? env.PROMETHEUS_MEMORY_BLOB_DIR
24
+ : join(homedir(), ".prometheus", "memory", "blobs");
25
+ }
26
+
27
+ export function computeSha256(data: Uint8Array | string): string {
28
+ return createHash("sha256").update(data).digest("hex");
29
+ }
30
+
31
+ export function isDataUri(content: string): boolean {
32
+ return content.startsWith("data:");
33
+ }
34
+
35
+ export function parseDataUri(content: string): [mimeType: string, raw: Buffer] | null {
36
+ const match = DATA_URI_RE.exec(content);
37
+ if (match?.groups === undefined) return null;
38
+
39
+ const mimeType = match.groups.mime ?? "application/octet-stream";
40
+ const payload = match.groups.payload ?? "";
41
+ if (!isValidBase64(payload)) return null;
42
+
43
+ return [mimeType, Buffer.from(payload, "base64")];
44
+ }
45
+
46
+ export function shannonEntropy(text: string): number {
47
+ if (text.length === 0) return 0.0;
48
+
49
+ const counts = new Map<string, number>();
50
+ for (const char of text) counts.set(char, (counts.get(char) ?? 0) + 1);
51
+
52
+ let entropy = 0.0;
53
+ for (const count of counts.values()) {
54
+ const p = count / text.length;
55
+ entropy -= p * Math.log2(p);
56
+ }
57
+ return entropy;
58
+ }
59
+
60
+ export function looksLikeBase64Blob(content: string): boolean {
61
+ if (content.length < SIZE_BASE64_CHECK) return false;
62
+ return shannonEntropy(content) > ENTROPY_THRESHOLD;
63
+ }
64
+
65
+ export function storeBlob(rawBytes: Uint8Array): string {
66
+ const sha256 = computeSha256(rawBytes);
67
+ const blobDir = join(blobRoot(), sha256.slice(0, 2), sha256.slice(0, 4));
68
+ mkdirSync(blobDir, { recursive: true });
69
+ const blobPath = join(blobDir, sha256);
70
+ if (!existsSync(blobPath)) writeFileSync(blobPath, rawBytes);
71
+ return sha256;
72
+ }
73
+
74
+ export function sanitizeContent(content: string): [sanitizedContent: string, blobMetadata: BlobMetadata] {
75
+ const originalSize = Buffer.byteLength(content, "utf8");
76
+
77
+ if (isDataUri(content)) {
78
+ const parsed = parseDataUri(content);
79
+ if (parsed !== null) {
80
+ const [mimeType, rawBytes] = parsed;
81
+ const sha256 = storeBlob(rawBytes);
82
+ const blobRef = `blob://sha256/${sha256}`;
83
+ return [
84
+ `[Binary content extracted: ${mimeType}, ${rawBytes.length.toLocaleString("en-US")} bytes → ${blobRef}]`,
85
+ {
86
+ blob_ref: blobRef,
87
+ original_size: rawBytes.length,
88
+ mime: mimeType,
89
+ extraction_reason: "data_uri",
90
+ },
91
+ ];
92
+ }
93
+ }
94
+
95
+ if (originalSize > SIZE_HARD_CAP) {
96
+ const rawBytes = Buffer.from(content, "utf8");
97
+ const sha256 = storeBlob(rawBytes);
98
+ const blobRef = `blob://sha256/${sha256}`;
99
+ return [
100
+ `[Large content extracted: ${originalSize.toLocaleString("en-US")} bytes → ${blobRef}]`,
101
+ {
102
+ blob_ref: blobRef,
103
+ original_size: originalSize,
104
+ extraction_reason: "size_cap",
105
+ },
106
+ ];
107
+ }
108
+
109
+ if (originalSize > SIZE_BASE64_CHECK && looksLikeBase64Blob(content)) {
110
+ const rawBytes = Buffer.from(content, "utf8");
111
+ const sha256 = storeBlob(rawBytes);
112
+ const entropy = Math.round(shannonEntropy(content) * 100) / 100;
113
+ const blobRef = `blob://sha256/${sha256}`;
114
+ return [
115
+ `[Encoded content extracted: ${originalSize.toLocaleString("en-US")} bytes, entropy ${entropy.toFixed(1)} bits/char → ${blobRef}]`,
116
+ {
117
+ blob_ref: blobRef,
118
+ original_size: originalSize,
119
+ entropy,
120
+ extraction_reason: "high_entropy",
121
+ },
122
+ ];
123
+ }
124
+
125
+ return [content, {}];
126
+ }
127
+ function isValidBase64(payload: string): boolean {
128
+ if (payload.length % 4 !== 0) return false;
129
+ if (!BASE64_RE.test(payload)) return false;
130
+ try {
131
+ Buffer.from(payload, "base64");
132
+ return true;
133
+ } catch {
134
+ return false;
135
+ }
136
+ }
@@ -0,0 +1,103 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { mkdirSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+
6
+ export const DEFAULT_LOG_DIR = join(homedir(), ".prometheus", "memory", "data");
7
+ export const DEFAULT_LOG_DB = join(DEFAULT_LOG_DIR, "cost_log.db");
8
+
9
+ export interface CostStats {
10
+ total_calls: number;
11
+ total_memories_injected: number;
12
+ total_tokens: number;
13
+ total_estimated_cost_usd: number;
14
+ }
15
+
16
+ type AggregateRow = {
17
+ calls: number | null;
18
+ total_memories: number | null;
19
+ total_tokens: number | null;
20
+ total_cost: number | null;
21
+ };
22
+
23
+ export function getConn(dbPath?: string): Database {
24
+ const path = dbPath ?? DEFAULT_LOG_DB;
25
+ mkdirSync(dirname(path), { recursive: true });
26
+ return new Database(path, { create: true, readwrite: true, strict: true });
27
+ }
28
+
29
+ export function initCostLog(dbPath?: string): void {
30
+ const conn = getConn(dbPath);
31
+ try {
32
+ conn.run(`
33
+ CREATE TABLE IF NOT EXISTS cost_entries (
34
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
35
+ session_id TEXT,
36
+ memory_count INTEGER,
37
+ token_count INTEGER,
38
+ estimated_cost_usd REAL,
39
+ model TEXT DEFAULT 'default',
40
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
41
+ )
42
+ `);
43
+ } finally {
44
+ conn.close();
45
+ }
46
+ }
47
+ export function logCost(
48
+ sessionId: string,
49
+ memoryCount: number,
50
+ tokenCount: number,
51
+ estimatedCostUsd: number,
52
+ model = "default",
53
+ dbPath?: string,
54
+ ): void {
55
+ initCostLog(dbPath);
56
+ const conn = getConn(dbPath);
57
+ try {
58
+ conn
59
+ .query(`
60
+ INSERT INTO cost_entries (session_id, memory_count, token_count, estimated_cost_usd, model, timestamp)
61
+ VALUES (?, ?, ?, ?, ?, ?)
62
+ `)
63
+ .run(sessionId, memoryCount, tokenCount, estimatedCostUsd, model, localIsoTimestamp(new Date()));
64
+ } finally {
65
+ conn.close();
66
+ }
67
+ }
68
+ export function getCostStats(sessionId?: string, dbPath?: string): CostStats {
69
+ initCostLog(dbPath);
70
+ const conn = getConn(dbPath);
71
+ try {
72
+ const row = (
73
+ sessionId
74
+ ? conn
75
+ .query(`
76
+ SELECT COUNT(*) as calls, SUM(memory_count) as total_memories,
77
+ SUM(token_count) as total_tokens, SUM(estimated_cost_usd) as total_cost
78
+ FROM cost_entries WHERE session_id = ?
79
+ `)
80
+ .get(sessionId)
81
+ : conn
82
+ .query(`
83
+ SELECT COUNT(*) as calls, SUM(memory_count) as total_memories,
84
+ SUM(token_count) as total_tokens, SUM(estimated_cost_usd) as total_cost
85
+ FROM cost_entries
86
+ `)
87
+ .get()
88
+ ) as AggregateRow | null;
89
+
90
+ return {
91
+ total_calls: row?.calls ?? 0,
92
+ total_memories_injected: row?.total_memories ?? 0,
93
+ total_tokens: row?.total_tokens ?? 0,
94
+ total_estimated_cost_usd: Math.round((row?.total_cost ?? 0) * 1_000_000) / 1_000_000,
95
+ };
96
+ } finally {
97
+ conn.close();
98
+ }
99
+ }
100
+ function localIsoTimestamp(date: Date): string {
101
+ const offsetMs = date.getTimezoneOffset() * 60_000;
102
+ return new Date(date.getTime() - offsetMs).toISOString().replace("Z", "");
103
+ }