@psiclawops/hypermem 0.1.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 (94) hide show
  1. package/ARCHITECTURE.md +296 -0
  2. package/LICENSE +190 -0
  3. package/README.md +243 -0
  4. package/dist/background-indexer.d.ts +117 -0
  5. package/dist/background-indexer.d.ts.map +1 -0
  6. package/dist/background-indexer.js +732 -0
  7. package/dist/compaction-fence.d.ts +89 -0
  8. package/dist/compaction-fence.d.ts.map +1 -0
  9. package/dist/compaction-fence.js +153 -0
  10. package/dist/compositor.d.ts +139 -0
  11. package/dist/compositor.d.ts.map +1 -0
  12. package/dist/compositor.js +1109 -0
  13. package/dist/cross-agent.d.ts +57 -0
  14. package/dist/cross-agent.d.ts.map +1 -0
  15. package/dist/cross-agent.js +254 -0
  16. package/dist/db.d.ts +131 -0
  17. package/dist/db.d.ts.map +1 -0
  18. package/dist/db.js +398 -0
  19. package/dist/desired-state-store.d.ts +100 -0
  20. package/dist/desired-state-store.d.ts.map +1 -0
  21. package/dist/desired-state-store.js +212 -0
  22. package/dist/doc-chunk-store.d.ts +115 -0
  23. package/dist/doc-chunk-store.d.ts.map +1 -0
  24. package/dist/doc-chunk-store.js +278 -0
  25. package/dist/doc-chunker.d.ts +99 -0
  26. package/dist/doc-chunker.d.ts.map +1 -0
  27. package/dist/doc-chunker.js +324 -0
  28. package/dist/episode-store.d.ts +48 -0
  29. package/dist/episode-store.d.ts.map +1 -0
  30. package/dist/episode-store.js +135 -0
  31. package/dist/fact-store.d.ts +57 -0
  32. package/dist/fact-store.d.ts.map +1 -0
  33. package/dist/fact-store.js +175 -0
  34. package/dist/fleet-store.d.ts +144 -0
  35. package/dist/fleet-store.d.ts.map +1 -0
  36. package/dist/fleet-store.js +276 -0
  37. package/dist/hybrid-retrieval.d.ts +60 -0
  38. package/dist/hybrid-retrieval.d.ts.map +1 -0
  39. package/dist/hybrid-retrieval.js +340 -0
  40. package/dist/index.d.ts +611 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +1042 -0
  43. package/dist/knowledge-graph.d.ts +110 -0
  44. package/dist/knowledge-graph.d.ts.map +1 -0
  45. package/dist/knowledge-graph.js +305 -0
  46. package/dist/knowledge-store.d.ts +72 -0
  47. package/dist/knowledge-store.d.ts.map +1 -0
  48. package/dist/knowledge-store.js +241 -0
  49. package/dist/library-schema.d.ts +22 -0
  50. package/dist/library-schema.d.ts.map +1 -0
  51. package/dist/library-schema.js +717 -0
  52. package/dist/message-store.d.ts +76 -0
  53. package/dist/message-store.d.ts.map +1 -0
  54. package/dist/message-store.js +273 -0
  55. package/dist/preference-store.d.ts +54 -0
  56. package/dist/preference-store.d.ts.map +1 -0
  57. package/dist/preference-store.js +109 -0
  58. package/dist/preservation-gate.d.ts +82 -0
  59. package/dist/preservation-gate.d.ts.map +1 -0
  60. package/dist/preservation-gate.js +150 -0
  61. package/dist/provider-translator.d.ts +40 -0
  62. package/dist/provider-translator.d.ts.map +1 -0
  63. package/dist/provider-translator.js +349 -0
  64. package/dist/rate-limiter.d.ts +76 -0
  65. package/dist/rate-limiter.d.ts.map +1 -0
  66. package/dist/rate-limiter.js +179 -0
  67. package/dist/redis.d.ts +188 -0
  68. package/dist/redis.d.ts.map +1 -0
  69. package/dist/redis.js +534 -0
  70. package/dist/schema.d.ts +15 -0
  71. package/dist/schema.d.ts.map +1 -0
  72. package/dist/schema.js +203 -0
  73. package/dist/secret-scanner.d.ts +51 -0
  74. package/dist/secret-scanner.d.ts.map +1 -0
  75. package/dist/secret-scanner.js +248 -0
  76. package/dist/seed.d.ts +108 -0
  77. package/dist/seed.d.ts.map +1 -0
  78. package/dist/seed.js +177 -0
  79. package/dist/system-store.d.ts +73 -0
  80. package/dist/system-store.d.ts.map +1 -0
  81. package/dist/system-store.js +182 -0
  82. package/dist/topic-store.d.ts +45 -0
  83. package/dist/topic-store.d.ts.map +1 -0
  84. package/dist/topic-store.js +136 -0
  85. package/dist/types.d.ts +329 -0
  86. package/dist/types.d.ts.map +1 -0
  87. package/dist/types.js +9 -0
  88. package/dist/vector-store.d.ts +132 -0
  89. package/dist/vector-store.d.ts.map +1 -0
  90. package/dist/vector-store.js +498 -0
  91. package/dist/work-store.d.ts +112 -0
  92. package/dist/work-store.d.ts.map +1 -0
  93. package/dist/work-store.js +273 -0
  94. package/package.json +57 -0
@@ -0,0 +1,241 @@
1
+ /**
2
+ * HyperMem Knowledge Store
3
+ *
4
+ * Long-term structured knowledge — replaces MEMORY.md.
5
+ * Lives in the central library DB.
6
+ * Knowledge entries are keyed (domain + key), versioned via superseded_by,
7
+ * and linked to each other via knowledge_links.
8
+ */
9
+ function nowIso() {
10
+ return new Date().toISOString();
11
+ }
12
+ function parseKnowledgeRow(row) {
13
+ return {
14
+ id: row.id,
15
+ agentId: row.agent_id,
16
+ domain: row.domain,
17
+ key: row.key,
18
+ content: row.content,
19
+ confidence: row.confidence,
20
+ sourceType: row.source_type,
21
+ sourceRef: row.source_ref || null,
22
+ createdAt: row.created_at,
23
+ updatedAt: row.updated_at,
24
+ expiresAt: row.expires_at || null,
25
+ supersededBy: row.superseded_by || null,
26
+ };
27
+ }
28
+ export class KnowledgeStore {
29
+ db;
30
+ constructor(db) {
31
+ this.db = db;
32
+ }
33
+ /**
34
+ * Upsert a knowledge entry.
35
+ *
36
+ * Versioning semantics:
37
+ * - If no active entry exists: insert as version 1
38
+ * - If same content: refresh confidence + timestamp only (no new version)
39
+ * - If different content: insert as new version (max_version + 1), mark
40
+ * previous active row as superseded_by = new_id
41
+ *
42
+ * This guarantees version history is real rows, not in-place overwrites.
43
+ * The unique constraint is (agent_id, domain, key, version) so each
44
+ * version is a distinct row.
45
+ */
46
+ upsert(agentId, domain, key, content, opts) {
47
+ const now = nowIso();
48
+ const sourceType = opts?.sourceType || 'manual';
49
+ const confidence = opts?.confidence ?? 1.0;
50
+ const visibility = opts?.visibility ?? 'private';
51
+ // Find current active entry (not superseded, not expired)
52
+ const existing = this.db.prepare(`
53
+ SELECT * FROM knowledge
54
+ WHERE agent_id = ? AND domain = ? AND key = ?
55
+ AND superseded_by IS NULL
56
+ AND (expires_at IS NULL OR expires_at > datetime('now'))
57
+ ORDER BY version DESC LIMIT 1
58
+ `).get(agentId, domain, key);
59
+ if (existing && existing.content === content) {
60
+ // Same content — refresh confidence and timestamp only, no new version
61
+ this.db.prepare('UPDATE knowledge SET confidence = ?, updated_at = ? WHERE id = ?').run(confidence, now, existing.id);
62
+ return parseKnowledgeRow({ ...existing, confidence, updated_at: now });
63
+ }
64
+ // Determine next version number
65
+ const maxVersionRow = this.db.prepare(`
66
+ SELECT MAX(version) AS max_version FROM knowledge
67
+ WHERE agent_id = ? AND domain = ? AND key = ?
68
+ `).get(agentId, domain, key);
69
+ const nextVersion = (maxVersionRow?.max_version ?? 0) + 1;
70
+ // Insert new version row (no ON CONFLICT — version column ensures uniqueness)
71
+ const result = this.db.prepare(`
72
+ INSERT INTO knowledge
73
+ (agent_id, domain, key, version, content, confidence, visibility, source_type, source_ref,
74
+ created_at, updated_at, expires_at)
75
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
76
+ `).run(agentId, domain, key, nextVersion, content, confidence, visibility, sourceType, opts?.sourceRef ?? null, now, now, opts?.expiresAt ?? null);
77
+ const newId = result.lastInsertRowid;
78
+ // Mark previous active entry as superseded by this new version
79
+ if (existing) {
80
+ this.db.prepare('UPDATE knowledge SET superseded_by = ?, updated_at = ? WHERE id = ?').run(newId, now, existing.id);
81
+ // Link: new version supersedes old version
82
+ this.addLink(newId, existing.id, 'supersedes');
83
+ }
84
+ return {
85
+ id: newId,
86
+ agentId,
87
+ domain,
88
+ key,
89
+ content,
90
+ confidence,
91
+ sourceType,
92
+ sourceRef: opts?.sourceRef ?? null,
93
+ createdAt: now,
94
+ updatedAt: now,
95
+ expiresAt: opts?.expiresAt ?? null,
96
+ supersededBy: null,
97
+ };
98
+ }
99
+ /**
100
+ * Get current (non-superseded) knowledge for an agent.
101
+ */
102
+ getActive(agentId, opts) {
103
+ let sql = `
104
+ SELECT * FROM knowledge
105
+ WHERE agent_id = ?
106
+ AND superseded_by IS NULL
107
+ AND (expires_at IS NULL OR expires_at > datetime('now'))
108
+ `;
109
+ const params = [agentId];
110
+ if (opts?.domain) {
111
+ sql += ' AND domain = ?';
112
+ params.push(opts.domain);
113
+ }
114
+ sql += ' ORDER BY domain, key';
115
+ if (opts?.limit) {
116
+ sql += ' LIMIT ?';
117
+ params.push(opts.limit);
118
+ }
119
+ const rows = this.db.prepare(sql).all(...params);
120
+ return rows.map(parseKnowledgeRow);
121
+ }
122
+ /**
123
+ * Get a specific knowledge entry by domain + key.
124
+ */
125
+ get(agentId, domain, key) {
126
+ const row = this.db.prepare(`
127
+ SELECT * FROM knowledge
128
+ WHERE agent_id = ? AND domain = ? AND key = ?
129
+ AND superseded_by IS NULL
130
+ `).get(agentId, domain, key);
131
+ return row ? parseKnowledgeRow(row) : null;
132
+ }
133
+ /**
134
+ * Get the version history of a knowledge entry.
135
+ */
136
+ getHistory(agentId, domain, key) {
137
+ const rows = this.db.prepare(`
138
+ SELECT * FROM knowledge
139
+ WHERE agent_id = ? AND domain = ? AND key = ?
140
+ ORDER BY created_at DESC
141
+ `).all(agentId, domain, key);
142
+ return rows.map(parseKnowledgeRow);
143
+ }
144
+ /**
145
+ * Search knowledge by content.
146
+ */
147
+ search(agentId, query, limit = 20) {
148
+ const rows = this.db.prepare(`
149
+ SELECT * FROM knowledge
150
+ WHERE agent_id = ?
151
+ AND superseded_by IS NULL
152
+ AND (content LIKE ? OR key LIKE ?)
153
+ ORDER BY confidence DESC
154
+ LIMIT ?
155
+ `).all(agentId, `%${query}%`, `%${query}%`, limit);
156
+ return rows.map(parseKnowledgeRow);
157
+ }
158
+ /**
159
+ * List all domains for an agent.
160
+ */
161
+ getDomains(agentId) {
162
+ const rows = this.db.prepare(`
163
+ SELECT DISTINCT domain FROM knowledge
164
+ WHERE agent_id = ? AND superseded_by IS NULL
165
+ ORDER BY domain
166
+ `).all(agentId);
167
+ return rows.map(r => r.domain);
168
+ }
169
+ /**
170
+ * Add a link between knowledge entries.
171
+ */
172
+ addLink(fromId, toId, linkType) {
173
+ this.db.prepare(`
174
+ INSERT OR IGNORE INTO knowledge_links (from_type, from_id, to_type, to_id, link_type, created_at)
175
+ VALUES ('knowledge', ?, 'knowledge', ?, ?, ?)
176
+ `).run(fromId, toId, linkType, nowIso());
177
+ }
178
+ /**
179
+ * Get knowledge count.
180
+ */
181
+ getCount(agentId) {
182
+ const row = this.db.prepare('SELECT COUNT(*) AS count FROM knowledge WHERE agent_id = ? AND superseded_by IS NULL').get(agentId);
183
+ return row.count;
184
+ }
185
+ /**
186
+ * Import from MEMORY.md content.
187
+ * Parses markdown sections into domain/key/content entries.
188
+ */
189
+ importFromMarkdown(agentId, markdown, sourcePath) {
190
+ const lines = markdown.split('\n');
191
+ let currentDomain = 'general';
192
+ let currentKey = '';
193
+ let currentContent = [];
194
+ let imported = 0;
195
+ const flush = () => {
196
+ if (currentKey && currentContent.length > 0) {
197
+ this.upsert(agentId, currentDomain, currentKey, currentContent.join('\n').trim(), {
198
+ sourceType: 'manual',
199
+ sourceRef: sourcePath,
200
+ });
201
+ imported++;
202
+ }
203
+ currentContent = [];
204
+ };
205
+ for (const line of lines) {
206
+ // ## Section = domain
207
+ if (line.startsWith('## ')) {
208
+ flush();
209
+ currentDomain = line.replace('## ', '').trim().toLowerCase().replace(/\s+/g, '_');
210
+ currentKey = '';
211
+ continue;
212
+ }
213
+ // ### Subsection or **Bold** = key
214
+ if (line.startsWith('### ')) {
215
+ flush();
216
+ currentKey = line.replace('### ', '').trim();
217
+ continue;
218
+ }
219
+ // - **Key:** Value pattern
220
+ const kvMatch = line.match(/^[-*]\s+\*\*(.+?)\*\*[:\s]+(.+)/);
221
+ if (kvMatch) {
222
+ flush();
223
+ currentKey = kvMatch[1].trim();
224
+ currentContent.push(kvMatch[2].trim());
225
+ continue;
226
+ }
227
+ // Regular content line
228
+ if (currentKey) {
229
+ currentContent.push(line);
230
+ }
231
+ else if (line.trim()) {
232
+ // Content without a key — use line hash as key
233
+ currentKey = `note_${lines.indexOf(line)}`;
234
+ currentContent.push(line);
235
+ }
236
+ }
237
+ flush();
238
+ return imported;
239
+ }
240
+ }
241
+ //# sourceMappingURL=knowledge-store.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * HyperMem Library Schema — Fleet-Wide Structured Knowledge
3
+ *
4
+ * Single database: ~/.openclaw/hypermem/library.db
5
+ * The "crown jewel" — durable, backed up, low-write-frequency.
6
+ *
7
+ * Collections:
8
+ * 1. Library entries (versioned docs, specs, reference material)
9
+ * 2. Facts (agent-learned truths)
10
+ * 3. Preferences (behavioral patterns)
11
+ * 4. Knowledge (structured domain knowledge, supersedable)
12
+ * 5. Episodes (significant events)
13
+ * 6. Fleet registry (agents, orgs)
14
+ * 7. System registry (server state, config)
15
+ * 8. Session registry (lifecycle tracking)
16
+ * 9. Work items (fleet kanban)
17
+ * 10. Topics (cross-session thread tracking)
18
+ */
19
+ import type { DatabaseSync } from 'node:sqlite';
20
+ export declare const LIBRARY_SCHEMA_VERSION = 7;
21
+ export declare function migrateLibrary(db: DatabaseSync): void;
22
+ //# sourceMappingURL=library-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"library-schema.d.ts","sourceRoot":"","sources":["../src/library-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,eAAO,MAAM,sBAAsB,IAAI,CAAC;AA4rBxC,wBAAgB,cAAc,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA8DrD"}