@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,211 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import { existsSync, writeFileSync } from "node:fs";
3
+ import { closeQuietly, type DatabasePath, openDatabase } from "../../db";
4
+
5
+ export const ANNOTATION_KINDS = ["mentions", "fact", "occurred_on", "has_source"] as const;
6
+ export type AnnotationKind = (typeof ANNOTATION_KINDS)[number];
7
+
8
+ export interface MigrationOptions {
9
+ readonly dbPath: DatabasePath;
10
+ readonly dryRun?: boolean;
11
+ readonly backup?: boolean;
12
+ readonly logFn?: (line: string) => void;
13
+ }
14
+
15
+ export interface PendingConnection {
16
+ query<T = unknown>(sql: string): { get(...params: unknown[]): T | null };
17
+ }
18
+ type SerializableDatabase = Database & { serialize(): Uint8Array };
19
+
20
+ interface TripleCandidateRow {
21
+ id: number;
22
+ subject: string;
23
+ predicate: AnnotationKind;
24
+ object: string;
25
+ source: string | null;
26
+ confidence: number | null;
27
+ created_at: string | null;
28
+ }
29
+
30
+ interface Classification {
31
+ rows: TripleCandidateRow[];
32
+ total: number;
33
+ }
34
+
35
+ function placeholders(count: number): string {
36
+ return Array.from({ length: count }, () => "?").join(",");
37
+ }
38
+
39
+ function hasTable(db: Database, name: string): boolean {
40
+ return db.query("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(name) !== null;
41
+ }
42
+
43
+ function copyDatabase(source: DatabasePath, destination: string): void {
44
+ let db: Database | null = null;
45
+ try {
46
+ db = openDatabase(source, { create: false, readwrite: false, pragmas: false });
47
+ writeFileSync(destination, (db as SerializableDatabase).serialize());
48
+ } finally {
49
+ closeQuietly(db);
50
+ }
51
+ }
52
+
53
+ function initAnnotations(db: Database): void {
54
+ db.run(`
55
+ CREATE TABLE IF NOT EXISTS annotations (
56
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
57
+ memory_id TEXT NOT NULL,
58
+ kind TEXT NOT NULL,
59
+ value TEXT NOT NULL,
60
+ source TEXT,
61
+ confidence REAL DEFAULT 1.0,
62
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
63
+ )
64
+ `);
65
+ db.run("CREATE INDEX IF NOT EXISTS idx_annot_memory_kind ON annotations(memory_id, kind)");
66
+ db.run("CREATE INDEX IF NOT EXISTS idx_annot_kind_value ON annotations(kind, value)");
67
+ db.run("CREATE UNIQUE INDEX IF NOT EXISTS idx_annot_unique ON annotations(memory_id, kind, value)");
68
+ }
69
+
70
+ export function hasPendingMigration(db: Database): boolean {
71
+ if (!hasTable(db, "triples")) return false;
72
+ const marks = placeholders(ANNOTATION_KINDS.length);
73
+ if (!hasTable(db, "annotations")) {
74
+ return db.query(`SELECT 1 FROM triples WHERE predicate IN (${marks}) LIMIT 1`).get(...ANNOTATION_KINDS) !== null;
75
+ }
76
+ return (
77
+ db
78
+ .query(`
79
+ SELECT 1
80
+ FROM triples t
81
+ WHERE t.predicate IN (${marks})
82
+ AND NOT EXISTS (
83
+ SELECT 1 FROM annotations a
84
+ WHERE a.memory_id = t.subject
85
+ AND a.kind = t.predicate
86
+ AND a.value = t.object
87
+ )
88
+ LIMIT 1
89
+ `)
90
+ .get(...ANNOTATION_KINDS) !== null
91
+ );
92
+ }
93
+ function classifyRows(db: Database): Classification {
94
+ if (!hasTable(db, "triples")) return { rows: [], total: 0 };
95
+ const totalRow = db.query("SELECT COUNT(*) AS count FROM triples").get() as { count: number };
96
+ const marks = placeholders(ANNOTATION_KINDS.length);
97
+ const candidates = db
98
+ .query(`
99
+ SELECT id, subject, predicate, object, source, confidence, created_at
100
+ FROM triples
101
+ WHERE predicate IN (${marks})
102
+ ORDER BY id ASC
103
+ `)
104
+ .all(...ANNOTATION_KINDS) as TripleCandidateRow[];
105
+ if (!hasTable(db, "annotations")) return { rows: candidates, total: totalRow.count };
106
+ const rows = candidates.filter(row => {
107
+ return (
108
+ db
109
+ .query("SELECT 1 FROM annotations WHERE memory_id = ? AND kind = ? AND value = ? LIMIT 1")
110
+ .get(row.subject, row.predicate, row.object) === null
111
+ );
112
+ });
113
+ return { rows, total: totalRow.count };
114
+ }
115
+
116
+ function kindCounts(rows: readonly TripleCandidateRow[]): Record<string, number> {
117
+ const counts: Record<string, number> = {};
118
+ for (const row of rows) counts[row.predicate] = (counts[row.predicate] ?? 0) + 1;
119
+ return counts;
120
+ }
121
+
122
+ function migrateRows(db: Database, rows: readonly TripleCandidateRow[]): number {
123
+ if (rows.length === 0) return 0;
124
+ const insert = db.prepare(`
125
+ INSERT OR IGNORE INTO annotations (memory_id, kind, value, source, confidence, created_at)
126
+ VALUES (?, ?, ?, ?, ?, ?)
127
+ `);
128
+ let written = 0;
129
+ for (const row of rows) {
130
+ const result = insert.run(
131
+ row.subject,
132
+ row.predicate,
133
+ row.object,
134
+ row.source,
135
+ row.confidence ?? 1.0,
136
+ row.created_at,
137
+ ) as {
138
+ readonly changes: number;
139
+ };
140
+ written += result.changes;
141
+ }
142
+ return written;
143
+ }
144
+
145
+ export function migrate(
146
+ dbPathOrOptions: DatabasePath | MigrationOptions,
147
+ dryRun = false,
148
+ backup = true,
149
+ logFn: (line: string) => void = console.log,
150
+ ): number {
151
+ const options =
152
+ typeof dbPathOrOptions === "string" ? { dbPath: dbPathOrOptions, dryRun, backup, logFn } : dbPathOrOptions;
153
+ const dbPath = options.dbPath;
154
+ const effectiveDryRun = options.dryRun ?? false;
155
+ const effectiveBackup = options.backup ?? true;
156
+ const effectiveLog = options.logFn ?? console.log;
157
+ if (dbPath === ":memory:" || !existsSync(dbPath)) {
158
+ effectiveLog(`ERROR: database not found: ${dbPath}`);
159
+ throw new Error(`database not found: ${dbPath}`);
160
+ }
161
+
162
+ let db = openDatabase(dbPath);
163
+ let classified: Classification;
164
+ try {
165
+ classified = classifyRows(db);
166
+ } finally {
167
+ closeQuietly(db);
168
+ }
169
+
170
+ effectiveLog(`Database: ${dbPath}`);
171
+ effectiveLog(` triples rows (total): ${classified.total}`);
172
+ effectiveLog(` rows-to-migrate (this run): ${classified.rows.length}`);
173
+ if (classified.rows.length > 0) {
174
+ const counts = kindCounts(classified.rows);
175
+ for (const kind of Object.keys(counts).sort()) effectiveLog(` ${kind.padEnd(14, " ")} ${counts[kind]}`);
176
+ }
177
+ if (classified.rows.length === 0) {
178
+ effectiveLog("Nothing to migrate. Schema is already split or no annotation rows exist.");
179
+ return 0;
180
+ }
181
+ if (effectiveDryRun) {
182
+ effectiveLog("Dry run: no changes written.");
183
+ return classified.rows.length;
184
+ }
185
+ if (effectiveBackup) {
186
+ const backupPath = `${dbPath}.pre_e6_backup`;
187
+ if (existsSync(backupPath)) effectiveLog(`Backup already exists at ${backupPath}; leaving as-is.`);
188
+ else {
189
+ copyDatabase(dbPath, backupPath);
190
+ effectiveLog(`Backup written to ${backupPath}`);
191
+ }
192
+ }
193
+
194
+ db = openDatabase(dbPath);
195
+ try {
196
+ db.run("BEGIN IMMEDIATE");
197
+ try {
198
+ initAnnotations(db);
199
+ const lockedClassification = classifyRows(db);
200
+ const written = migrateRows(db, lockedClassification.rows);
201
+ db.run("COMMIT");
202
+ effectiveLog(`Migration complete: ${written} rows moved to annotations table.`);
203
+ return written;
204
+ } catch (error) {
205
+ db.run("ROLLBACK");
206
+ throw error;
207
+ }
208
+ } finally {
209
+ closeQuietly(db);
210
+ }
211
+ }
@@ -0,0 +1 @@
1
+ export * from "./e6-triplestore-split";
@@ -0,0 +1,71 @@
1
+ export interface MmrResult {
2
+ readonly content?: string;
3
+ readonly score?: number;
4
+ readonly [key: string]: unknown;
5
+ }
6
+
7
+ export type SimilarityFn = (textA: string, textB: string) => number;
8
+
9
+ export function jaccardSimilarity(textA: string, textB: string): number {
10
+ const wordsA = new Set(textA.toLowerCase().split(/\s+/).filter(Boolean));
11
+ const wordsB = new Set(textB.toLowerCase().split(/\s+/).filter(Boolean));
12
+
13
+ if (wordsA.size === 0 || wordsB.size === 0) return 0.0;
14
+
15
+ let intersection = 0;
16
+ for (const word of wordsA) {
17
+ if (wordsB.has(word)) intersection += 1;
18
+ }
19
+
20
+ return intersection / (wordsA.size + wordsB.size - intersection);
21
+ }
22
+ export function mmrRerank<T extends MmrResult>(
23
+ results: readonly T[],
24
+ lambdaParam = 0.7,
25
+ topK = 10,
26
+ similarityFn: SimilarityFn = jaccardSimilarity,
27
+ ): T[] {
28
+ const limit = Math.max(0, Math.trunc(topK));
29
+ if (limit <= 0) return [];
30
+ if (results.length <= 1) return results.slice(0, limit);
31
+
32
+ const sortedResults = results.slice().sort((left, right) => (right.score ?? 0) - (left.score ?? 0));
33
+ const first = sortedResults[0];
34
+ if (first === undefined) return [];
35
+
36
+ const selected: T[] = [first];
37
+ const remaining = sortedResults.slice(1);
38
+
39
+ while (remaining.length > 0 && selected.length < limit) {
40
+ let bestIdx = 0;
41
+ let bestScore = Number.NEGATIVE_INFINITY;
42
+
43
+ for (let idx = 0; idx < remaining.length; idx += 1) {
44
+ const candidate = remaining[idx];
45
+ if (candidate === undefined) continue;
46
+
47
+ let maxSimilarity = 0.0;
48
+ const candidateContent = candidate.content ?? "";
49
+ for (const selectedResult of selected) {
50
+ const similarity = similarityFn(candidateContent, selectedResult.content ?? "");
51
+ if (similarity > maxSimilarity) maxSimilarity = similarity;
52
+ }
53
+
54
+ const relevance = candidate.score ?? 0;
55
+ const mmrScore = lambdaParam * relevance - (1.0 - lambdaParam) * maxSimilarity;
56
+ if (mmrScore > bestScore) {
57
+ bestScore = mmrScore;
58
+ bestIdx = idx;
59
+ }
60
+ }
61
+
62
+ const chosen = remaining.splice(bestIdx, 1)[0];
63
+ if (chosen !== undefined) selected.push(chosen);
64
+ }
65
+
66
+ if (selected.length < limit) {
67
+ selected.push(...remaining.slice(0, limit - selected.length));
68
+ }
69
+
70
+ return selected;
71
+ }
@@ -0,0 +1,62 @@
1
+ import type { BeamMemoryState, RecallOptions, RecallResult } from "./beam/types";
2
+ import { embedQuery } from "./embeddings";
3
+ import {
4
+ type PolyphonicMemoryResult,
5
+ type PolyphonicRecallOptions,
6
+ polyphonicRecall,
7
+ polyphonicRecallIsEnabled,
8
+ } from "./polyphonic-recall";
9
+
10
+ export interface OrchestratorBeam extends BeamMemoryState {
11
+ recall?: (query: string, topK?: number, options?: RecallOptions) => Promise<RecallResult[]>;
12
+ recallEnhanced?: (query: string, topK?: number, options?: RecallOptions) => Promise<RecallResult[]>;
13
+ }
14
+
15
+ export interface OrchestrateRecallOptions
16
+ extends Omit<RecallOptions, "queryEmbedding">,
17
+ Omit<PolyphonicRecallOptions, "queryEmbedding"> {
18
+ readonly queryEmbedding?: readonly number[] | Float32Array | null;
19
+ readonly enhanced?: boolean;
20
+ readonly forcePolyphonic?: boolean;
21
+ readonly forceLinear?: boolean;
22
+ }
23
+
24
+ export interface OrchestratedRecallResult extends Omit<RecallResult, "metadata" | "score" | "tier"> {
25
+ score?: number;
26
+ metadata?: RecallResult["metadata"];
27
+ tier?: RecallResult["tier"] | PolyphonicMemoryResult["tier"];
28
+ combined_score?: PolyphonicMemoryResult["combined_score"];
29
+ voice_scores?: PolyphonicMemoryResult["voice_scores"];
30
+ }
31
+
32
+ function toLinearRecallOptions(options: OrchestrateRecallOptions): RecallOptions {
33
+ if (options.queryEmbedding instanceof Float32Array) {
34
+ return { ...options, queryEmbedding: Array.from(options.queryEmbedding) };
35
+ }
36
+ return options as RecallOptions;
37
+ }
38
+
39
+ export async function orchestrateRecall(
40
+ beam: OrchestratorBeam,
41
+ query: string,
42
+ topK = 20,
43
+ options: OrchestrateRecallOptions = {},
44
+ ): Promise<OrchestratedRecallResult[]> {
45
+ const polyphonic = !options.forceLinear && (options.forcePolyphonic === true || polyphonicRecallIsEnabled());
46
+ let queryEmbedding: readonly number[] | Float32Array | null | undefined = options.queryEmbedding;
47
+ if (queryEmbedding === undefined && query.length > 0) {
48
+ // Auto-derive when the caller did not pass one. `embedQuery()` returns null when
49
+ // embeddings are disabled or no provider is configured, so this is a no-op for
50
+ // FTS-only deployments. `null` (explicit "no embedding") is preserved untouched.
51
+ queryEmbedding = await embedQuery(query);
52
+ }
53
+ if (polyphonic) {
54
+ return polyphonicRecall(beam, query, topK, { ...options, queryEmbedding });
55
+ }
56
+ const linearOptions = toLinearRecallOptions({ ...options, queryEmbedding });
57
+ if (options.enhanced === true && typeof beam.recallEnhanced === "function") {
58
+ return beam.recallEnhanced(query, topK, linearOptions);
59
+ }
60
+ if (typeof beam.recall === "function") return beam.recall(query, topK, linearOptions);
61
+ return [];
62
+ }