@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.
- package/ARCHITECTURE.md +296 -0
- package/LICENSE +190 -0
- package/README.md +243 -0
- package/dist/background-indexer.d.ts +117 -0
- package/dist/background-indexer.d.ts.map +1 -0
- package/dist/background-indexer.js +732 -0
- package/dist/compaction-fence.d.ts +89 -0
- package/dist/compaction-fence.d.ts.map +1 -0
- package/dist/compaction-fence.js +153 -0
- package/dist/compositor.d.ts +139 -0
- package/dist/compositor.d.ts.map +1 -0
- package/dist/compositor.js +1109 -0
- package/dist/cross-agent.d.ts +57 -0
- package/dist/cross-agent.d.ts.map +1 -0
- package/dist/cross-agent.js +254 -0
- package/dist/db.d.ts +131 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +398 -0
- package/dist/desired-state-store.d.ts +100 -0
- package/dist/desired-state-store.d.ts.map +1 -0
- package/dist/desired-state-store.js +212 -0
- package/dist/doc-chunk-store.d.ts +115 -0
- package/dist/doc-chunk-store.d.ts.map +1 -0
- package/dist/doc-chunk-store.js +278 -0
- package/dist/doc-chunker.d.ts +99 -0
- package/dist/doc-chunker.d.ts.map +1 -0
- package/dist/doc-chunker.js +324 -0
- package/dist/episode-store.d.ts +48 -0
- package/dist/episode-store.d.ts.map +1 -0
- package/dist/episode-store.js +135 -0
- package/dist/fact-store.d.ts +57 -0
- package/dist/fact-store.d.ts.map +1 -0
- package/dist/fact-store.js +175 -0
- package/dist/fleet-store.d.ts +144 -0
- package/dist/fleet-store.d.ts.map +1 -0
- package/dist/fleet-store.js +276 -0
- package/dist/hybrid-retrieval.d.ts +60 -0
- package/dist/hybrid-retrieval.d.ts.map +1 -0
- package/dist/hybrid-retrieval.js +340 -0
- package/dist/index.d.ts +611 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1042 -0
- package/dist/knowledge-graph.d.ts +110 -0
- package/dist/knowledge-graph.d.ts.map +1 -0
- package/dist/knowledge-graph.js +305 -0
- package/dist/knowledge-store.d.ts +72 -0
- package/dist/knowledge-store.d.ts.map +1 -0
- package/dist/knowledge-store.js +241 -0
- package/dist/library-schema.d.ts +22 -0
- package/dist/library-schema.d.ts.map +1 -0
- package/dist/library-schema.js +717 -0
- package/dist/message-store.d.ts +76 -0
- package/dist/message-store.d.ts.map +1 -0
- package/dist/message-store.js +273 -0
- package/dist/preference-store.d.ts +54 -0
- package/dist/preference-store.d.ts.map +1 -0
- package/dist/preference-store.js +109 -0
- package/dist/preservation-gate.d.ts +82 -0
- package/dist/preservation-gate.d.ts.map +1 -0
- package/dist/preservation-gate.js +150 -0
- package/dist/provider-translator.d.ts +40 -0
- package/dist/provider-translator.d.ts.map +1 -0
- package/dist/provider-translator.js +349 -0
- package/dist/rate-limiter.d.ts +76 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +179 -0
- package/dist/redis.d.ts +188 -0
- package/dist/redis.d.ts.map +1 -0
- package/dist/redis.js +534 -0
- package/dist/schema.d.ts +15 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +203 -0
- package/dist/secret-scanner.d.ts +51 -0
- package/dist/secret-scanner.d.ts.map +1 -0
- package/dist/secret-scanner.js +248 -0
- package/dist/seed.d.ts +108 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +177 -0
- package/dist/system-store.d.ts +73 -0
- package/dist/system-store.d.ts.map +1 -0
- package/dist/system-store.js +182 -0
- package/dist/topic-store.d.ts +45 -0
- package/dist/topic-store.d.ts.map +1 -0
- package/dist/topic-store.js +136 -0
- package/dist/types.d.ts +329 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/vector-store.d.ts +132 -0
- package/dist/vector-store.d.ts.map +1 -0
- package/dist/vector-store.js +498 -0
- package/dist/work-store.d.ts +112 -0
- package/dist/work-store.d.ts.map +1 -0
- package/dist/work-store.js +273 -0
- 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"}
|