@psiclawops/hypermem 0.5.0 → 0.5.1

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 (160) hide show
  1. package/dist/background-indexer.d.ts +132 -0
  2. package/dist/background-indexer.d.ts.map +1 -0
  3. package/dist/background-indexer.js +1044 -0
  4. package/dist/cache.d.ts +110 -0
  5. package/dist/cache.d.ts.map +1 -0
  6. package/dist/cache.js +495 -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 +226 -0
  11. package/dist/compositor.d.ts.map +1 -0
  12. package/dist/compositor.js +2558 -0
  13. package/dist/content-type-classifier.d.ts +41 -0
  14. package/dist/content-type-classifier.d.ts.map +1 -0
  15. package/dist/content-type-classifier.js +181 -0
  16. package/dist/cross-agent.d.ts +62 -0
  17. package/dist/cross-agent.d.ts.map +1 -0
  18. package/dist/cross-agent.js +259 -0
  19. package/dist/db.d.ts +131 -0
  20. package/dist/db.d.ts.map +1 -0
  21. package/dist/db.js +402 -0
  22. package/dist/desired-state-store.d.ts +100 -0
  23. package/dist/desired-state-store.d.ts.map +1 -0
  24. package/dist/desired-state-store.js +222 -0
  25. package/dist/doc-chunk-store.d.ts +140 -0
  26. package/dist/doc-chunk-store.d.ts.map +1 -0
  27. package/dist/doc-chunk-store.js +391 -0
  28. package/dist/doc-chunker.d.ts +99 -0
  29. package/dist/doc-chunker.d.ts.map +1 -0
  30. package/dist/doc-chunker.js +324 -0
  31. package/dist/dreaming-promoter.d.ts +86 -0
  32. package/dist/dreaming-promoter.d.ts.map +1 -0
  33. package/dist/dreaming-promoter.js +381 -0
  34. package/dist/episode-store.d.ts +49 -0
  35. package/dist/episode-store.d.ts.map +1 -0
  36. package/dist/episode-store.js +135 -0
  37. package/dist/fact-store.d.ts +75 -0
  38. package/dist/fact-store.d.ts.map +1 -0
  39. package/dist/fact-store.js +236 -0
  40. package/dist/fleet-store.d.ts +144 -0
  41. package/dist/fleet-store.d.ts.map +1 -0
  42. package/dist/fleet-store.js +276 -0
  43. package/dist/fos-mod.d.ts +178 -0
  44. package/dist/fos-mod.d.ts.map +1 -0
  45. package/dist/fos-mod.js +416 -0
  46. package/dist/hybrid-retrieval.d.ts +64 -0
  47. package/dist/hybrid-retrieval.d.ts.map +1 -0
  48. package/dist/hybrid-retrieval.js +344 -0
  49. package/dist/image-eviction.d.ts +49 -0
  50. package/dist/image-eviction.d.ts.map +1 -0
  51. package/dist/image-eviction.js +251 -0
  52. package/dist/index.d.ts +650 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +1072 -0
  55. package/dist/keystone-scorer.d.ts +51 -0
  56. package/dist/keystone-scorer.d.ts.map +1 -0
  57. package/dist/keystone-scorer.js +52 -0
  58. package/dist/knowledge-graph.d.ts +110 -0
  59. package/dist/knowledge-graph.d.ts.map +1 -0
  60. package/dist/knowledge-graph.js +305 -0
  61. package/dist/knowledge-lint.d.ts +29 -0
  62. package/dist/knowledge-lint.d.ts.map +1 -0
  63. package/dist/knowledge-lint.js +116 -0
  64. package/dist/knowledge-store.d.ts +72 -0
  65. package/dist/knowledge-store.d.ts.map +1 -0
  66. package/dist/knowledge-store.js +247 -0
  67. package/dist/library-schema.d.ts +22 -0
  68. package/dist/library-schema.d.ts.map +1 -0
  69. package/dist/library-schema.js +1038 -0
  70. package/dist/message-store.d.ts +89 -0
  71. package/dist/message-store.d.ts.map +1 -0
  72. package/dist/message-store.js +323 -0
  73. package/dist/metrics-dashboard.d.ts +114 -0
  74. package/dist/metrics-dashboard.d.ts.map +1 -0
  75. package/dist/metrics-dashboard.js +260 -0
  76. package/dist/obsidian-exporter.d.ts +57 -0
  77. package/dist/obsidian-exporter.d.ts.map +1 -0
  78. package/dist/obsidian-exporter.js +274 -0
  79. package/dist/obsidian-watcher.d.ts +147 -0
  80. package/dist/obsidian-watcher.d.ts.map +1 -0
  81. package/dist/obsidian-watcher.js +403 -0
  82. package/dist/open-domain.d.ts +46 -0
  83. package/dist/open-domain.d.ts.map +1 -0
  84. package/dist/open-domain.js +125 -0
  85. package/dist/preference-store.d.ts +54 -0
  86. package/dist/preference-store.d.ts.map +1 -0
  87. package/dist/preference-store.js +109 -0
  88. package/dist/preservation-gate.d.ts +82 -0
  89. package/dist/preservation-gate.d.ts.map +1 -0
  90. package/dist/preservation-gate.js +150 -0
  91. package/dist/proactive-pass.d.ts +63 -0
  92. package/dist/proactive-pass.d.ts.map +1 -0
  93. package/dist/proactive-pass.js +239 -0
  94. package/dist/profiles.d.ts +44 -0
  95. package/dist/profiles.d.ts.map +1 -0
  96. package/dist/profiles.js +227 -0
  97. package/dist/provider-translator.d.ts +50 -0
  98. package/dist/provider-translator.d.ts.map +1 -0
  99. package/dist/provider-translator.js +403 -0
  100. package/dist/rate-limiter.d.ts +76 -0
  101. package/dist/rate-limiter.d.ts.map +1 -0
  102. package/dist/rate-limiter.js +179 -0
  103. package/dist/repair-tool-pairs.d.ts +38 -0
  104. package/dist/repair-tool-pairs.d.ts.map +1 -0
  105. package/dist/repair-tool-pairs.js +138 -0
  106. package/dist/retrieval-policy.d.ts +51 -0
  107. package/dist/retrieval-policy.d.ts.map +1 -0
  108. package/dist/retrieval-policy.js +77 -0
  109. package/dist/schema.d.ts +15 -0
  110. package/dist/schema.d.ts.map +1 -0
  111. package/dist/schema.js +229 -0
  112. package/dist/secret-scanner.d.ts +51 -0
  113. package/dist/secret-scanner.d.ts.map +1 -0
  114. package/dist/secret-scanner.js +248 -0
  115. package/dist/seed.d.ts +108 -0
  116. package/dist/seed.d.ts.map +1 -0
  117. package/dist/seed.js +177 -0
  118. package/dist/session-flusher.d.ts +53 -0
  119. package/dist/session-flusher.d.ts.map +1 -0
  120. package/dist/session-flusher.js +69 -0
  121. package/dist/session-topic-map.d.ts +41 -0
  122. package/dist/session-topic-map.d.ts.map +1 -0
  123. package/dist/session-topic-map.js +77 -0
  124. package/dist/spawn-context.d.ts +54 -0
  125. package/dist/spawn-context.d.ts.map +1 -0
  126. package/dist/spawn-context.js +159 -0
  127. package/dist/system-store.d.ts +73 -0
  128. package/dist/system-store.d.ts.map +1 -0
  129. package/dist/system-store.js +182 -0
  130. package/dist/temporal-store.d.ts +80 -0
  131. package/dist/temporal-store.d.ts.map +1 -0
  132. package/dist/temporal-store.js +149 -0
  133. package/dist/topic-detector.d.ts +35 -0
  134. package/dist/topic-detector.d.ts.map +1 -0
  135. package/dist/topic-detector.js +249 -0
  136. package/dist/topic-store.d.ts +45 -0
  137. package/dist/topic-store.d.ts.map +1 -0
  138. package/dist/topic-store.js +136 -0
  139. package/dist/topic-synthesizer.d.ts +51 -0
  140. package/dist/topic-synthesizer.d.ts.map +1 -0
  141. package/dist/topic-synthesizer.js +315 -0
  142. package/dist/trigger-registry.d.ts +63 -0
  143. package/dist/trigger-registry.d.ts.map +1 -0
  144. package/dist/trigger-registry.js +163 -0
  145. package/dist/types.d.ts +533 -0
  146. package/dist/types.d.ts.map +1 -0
  147. package/dist/types.js +9 -0
  148. package/dist/vector-store.d.ts +170 -0
  149. package/dist/vector-store.d.ts.map +1 -0
  150. package/dist/vector-store.js +677 -0
  151. package/dist/version.d.ts +34 -0
  152. package/dist/version.d.ts.map +1 -0
  153. package/dist/version.js +34 -0
  154. package/dist/wiki-page-emitter.d.ts +65 -0
  155. package/dist/wiki-page-emitter.d.ts.map +1 -0
  156. package/dist/wiki-page-emitter.js +258 -0
  157. package/dist/work-store.d.ts +112 -0
  158. package/dist/work-store.d.ts.map +1 -0
  159. package/dist/work-store.js +273 -0
  160. package/package.json +1 -1
package/dist/index.js ADDED
@@ -0,0 +1,1072 @@
1
+ /**
2
+ * hypermem — Agent-Centric Memory & Context Composition Engine
3
+ *
4
+ * @module @psiclawops/hypermem
5
+ *
6
+ * Architecture:
7
+ * L1: Redis — hot session working memory
8
+ * L2: messages.db — per-agent conversation log (rotatable)
9
+ * L3: vectors.db — per-agent semantic search index (reconstructable)
10
+ * L4: library.db — fleet-wide structured knowledge (crown jewel)
11
+ */
12
+ export { ENGINE_VERSION, MIN_NODE_VERSION, MIN_REDIS_VERSION, SQLITE_VEC_VERSION, MAIN_SCHEMA_VERSION, LIBRARY_SCHEMA_VERSION_EXPORT, HYPERMEM_COMPAT_VERSION, SCHEMA_COMPAT } from './version.js';
13
+ export { DatabaseManager } from './db.js';
14
+ export { MessageStore } from './message-store.js';
15
+ export { FactStore } from './fact-store.js';
16
+ export { KnowledgeStore } from './knowledge-store.js';
17
+ export { TopicStore } from './topic-store.js';
18
+ export { EpisodeStore } from './episode-store.js';
19
+ export { PreferenceStore } from './preference-store.js';
20
+ export { FleetStore } from './fleet-store.js';
21
+ export { SystemStore } from './system-store.js';
22
+ export { WorkStore } from './work-store.js';
23
+ export { DesiredStateStore } from './desired-state-store.js';
24
+ export { evictStaleContent, DEFAULT_EVICTION_CONFIG } from './image-eviction.js';
25
+ export { KnowledgeGraph } from './knowledge-graph.js';
26
+ export { RateLimiter, createRateLimitedEmbedder } from './rate-limiter.js';
27
+ export { CacheLayer } from './cache.js';
28
+ export { Compositor, applyToolGradientToWindow, canPersistReshapedHistory } from './compositor.js';
29
+ export { TRIGGER_REGISTRY, TRIGGER_REGISTRY_VERSION, TRIGGER_REGISTRY_HASH, DEFAULT_TRIGGERS, matchTriggers, } from './trigger-registry.js';
30
+ export { ensureCompactionFenceSchema, updateCompactionFence, getCompactionFence, getCompactionEligibility, getCompactableMessages, } from './compaction-fence.js';
31
+ export { verifyPreservation, verifyPreservationFromVectors, } from './preservation-gate.js';
32
+ export { toProviderFormat, fromProviderFormat, userMessageToNeutral, toolResultsToNeutral, normalizeToolCallId, generateToolCallId, detectProvider, repairToolCallPairs, } from './provider-translator.js';
33
+ export { migrate, SCHEMA_VERSION } from './schema.js';
34
+ export { migrateLibrary, LIBRARY_SCHEMA_VERSION } from './library-schema.js';
35
+ export { VectorStore, generateEmbeddings } from './vector-store.js';
36
+ export { hybridSearch, buildFtsQuery } from './hybrid-retrieval.js';
37
+ export { DocChunkStore } from './doc-chunk-store.js';
38
+ export { WorkspaceSeeder, seedWorkspace } from './seed.js';
39
+ export { chunkMarkdown, chunkFile, inferCollection, hashContent, ACA_COLLECTIONS } from './doc-chunker.js';
40
+ export { crossAgentQuery, canAccess, visibilityFilter, defaultOrgRegistry, buildOrgRegistryFromDb, loadOrgRegistryFromDb, } from './cross-agent.js';
41
+ export { BackgroundIndexer, createIndexer } from './background-indexer.js';
42
+ export { runDreamingPromoter, runDreamingPassForFleet, resolveAgentWorkspacePath, DEFAULT_DREAMER_CONFIG, } from './dreaming-promoter.js';
43
+ export { TopicSynthesizer } from './topic-synthesizer.js';
44
+ export { WikiPageEmitter } from './wiki-page-emitter.js';
45
+ export { lintKnowledge } from './knowledge-lint.js';
46
+ export { buildSpawnContext } from './spawn-context.js';
47
+ export { runNoiseSweep, runToolDecay } from './proactive-pass.js';
48
+ export { classifyContentType, signalWeight, isSignalBearing, SIGNAL_WEIGHT } from './content-type-classifier.js';
49
+ export { detectTopicShift, stripMessageMetadata } from './topic-detector.js';
50
+ export { SessionTopicMap } from './session-topic-map.js';
51
+ export { getActiveFOS, matchMOD, renderFOS, renderMOD, recordOutputMetrics, } from './fos-mod.js';
52
+ import { DatabaseManager } from './db.js';
53
+ import { MessageStore } from './message-store.js';
54
+ import { FactStore } from './fact-store.js';
55
+ import { KnowledgeStore } from './knowledge-store.js';
56
+ import { TopicStore } from './topic-store.js';
57
+ import { EpisodeStore } from './episode-store.js';
58
+ import { PreferenceStore } from './preference-store.js';
59
+ import { FleetStore } from './fleet-store.js';
60
+ import { SystemStore } from './system-store.js';
61
+ import { WorkStore } from './work-store.js';
62
+ import { KnowledgeGraph } from './knowledge-graph.js';
63
+ import { DesiredStateStore } from './desired-state-store.js';
64
+ import { CacheLayer } from './cache.js';
65
+ import { Compositor } from './compositor.js';
66
+ import { VectorStore } from './vector-store.js';
67
+ import { userMessageToNeutral, fromProviderFormat } from './provider-translator.js';
68
+ import { stripMessageMetadata } from './topic-detector.js';
69
+ import { DocChunkStore } from './doc-chunk-store.js';
70
+ import { WorkspaceSeeder } from './seed.js';
71
+ import { crossAgentQuery, buildOrgRegistryFromDb } from './cross-agent.js';
72
+ import path from 'node:path';
73
+ import os from 'node:os';
74
+ const DEFAULT_CONFIG = {
75
+ enabled: true,
76
+ dataDir: path.join(process.env.HOME || os.homedir(), '.openclaw', 'hypermem'),
77
+ cache: {
78
+ keyPrefix: 'hm:',
79
+ sessionTTL: 14400, // 4 hours — system/identity/meta slots
80
+ historyTTL: 604800, // 7 days — extended for ClawCanvas display
81
+ },
82
+ compositor: {
83
+ // TUNE-010 (2026-04-02): Raised from 65000 → 90000.
84
+ // TUNE-008 dropped to 65k as a tool-loop overflow band-aid. The real fix
85
+ // (tool-loop pass-through guard in assemble()) means tool turns don't
86
+ // re-run composition, so 90k is safe — leaves ~30k headroom for in-flight
87
+ // tool results on a 120k window. Budget is better spent on context quality.
88
+ defaultTokenBudget: 90000,
89
+ maxHistoryMessages: 1000,
90
+ maxFacts: 28,
91
+ maxCrossSessionContext: 6000,
92
+ maxRecentToolPairs: 3,
93
+ maxProseToolPairs: 10,
94
+ warmHistoryBudgetFraction: 0.4,
95
+ },
96
+ indexer: {
97
+ enabled: true,
98
+ factExtractionMode: 'tiered',
99
+ topicDormantAfter: '24h',
100
+ topicClosedAfter: '7d',
101
+ factDecayRate: 0.01,
102
+ episodeSignificanceThreshold: 0.5,
103
+ periodicInterval: 300000,
104
+ batchSize: 128,
105
+ maxMessagesPerTick: 500,
106
+ },
107
+ embedding: {
108
+ ollamaUrl: 'http://localhost:11434',
109
+ model: 'nomic-embed-text',
110
+ dimensions: 768,
111
+ timeout: 10000,
112
+ batchSize: 32,
113
+ },
114
+ };
115
+ /**
116
+ * hypermem — the main API facade.
117
+ *
118
+ * Usage:
119
+ * const hm = await hypermem.create({ dataDir: '~/.openclaw/hypermem' });
120
+ * await hm.record('forge', 'agent:forge:webchat:main', userMsg);
121
+ * const result = await hm.compose({ agentId: 'forge', sessionKey: '...', ... });
122
+ */
123
+ export class HyperMem {
124
+ dbManager;
125
+ cache;
126
+ compositor;
127
+ config;
128
+ constructor(config) {
129
+ this.config = config;
130
+ this.dbManager = new DatabaseManager({ dataDir: config.dataDir });
131
+ this.cache = new CacheLayer(config.cache);
132
+ this.compositor = new Compositor({
133
+ cache: this.cache,
134
+ vectorStore: null, // Set after create() when vector DB is available
135
+ libraryDb: null, // Set after create() when library DB is available
136
+ }, config.compositor);
137
+ }
138
+ /**
139
+ * Get the active vector store, if initialized.
140
+ * Used by the plugin to wire embeddings into the background indexer.
141
+ */
142
+ getVectorStore() {
143
+ return this.compositor.vectorStore;
144
+ }
145
+ /**
146
+ * Create and initialize a hypermem instance.
147
+ */
148
+ static async create(config) {
149
+ const merged = {
150
+ ...DEFAULT_CONFIG,
151
+ ...config,
152
+ cache: { ...DEFAULT_CONFIG.cache, ...config?.cache },
153
+ compositor: { ...DEFAULT_CONFIG.compositor, ...config?.compositor },
154
+ indexer: { ...DEFAULT_CONFIG.indexer, ...config?.indexer },
155
+ embedding: {
156
+ ...DEFAULT_CONFIG.embedding,
157
+ ...config?.embedding,
158
+ },
159
+ };
160
+ const hm = new HyperMem(merged);
161
+ const cacheOk = await hm.cache.connect();
162
+ if (cacheOk) {
163
+ console.log('[hypermem] Cache connected');
164
+ }
165
+ else {
166
+ console.warn('[hypermem] Cache unavailable — running in SQLite-only mode');
167
+ }
168
+ // ── Vector store init ─────────────────────────────────────
169
+ // Attempt to wire up sqlite-vec + nomic-embed-text for semantic recall.
170
+ // Non-fatal: if sqlite-vec isn't available or Ollama is down,
171
+ // hybridSearch() continues in FTS5-only mode.
172
+ // The vector store is shared (not per-agent) — facts/episodes from all agents
173
+ // are indexed together, keyed by (source_table, source_id).
174
+ try {
175
+ const vectorDb = hm.dbManager.getSharedVectorDb();
176
+ if (vectorDb) {
177
+ const vs = new VectorStore(vectorDb, merged.embedding, hm.dbManager.getLibraryDb());
178
+ vs.ensureTables();
179
+ hm.compositor.setVectorStore(vs);
180
+ const embeddingDesc = merged.embedding.provider === 'openai'
181
+ ? `${merged.embedding.openaiBaseUrl?.includes('openrouter') ? 'openrouter' : 'openai'}/${merged.embedding.model ?? 'text-embedding-3-small'}`
182
+ : `ollama/${merged.embedding.model ?? 'nomic-embed-text'}`;
183
+ console.log(`[hypermem] Vector store initialized (sqlite-vec + ${embeddingDesc})`);
184
+ }
185
+ else {
186
+ console.warn('[hypermem] sqlite-vec unavailable — semantic recall in FTS5-only mode');
187
+ }
188
+ }
189
+ catch (err) {
190
+ console.warn('[hypermem] Vector store init failed (non-fatal):', err.message);
191
+ }
192
+ return hm;
193
+ }
194
+ // ─── Core API (L2: Message DB) ──────────────────────────────
195
+ /**
196
+ * Record a user message.
197
+ */
198
+ async recordUserMessage(agentId, sessionKey, content, opts) {
199
+ const db = this.dbManager.getMessageDb(agentId);
200
+ this.dbManager.ensureAgent(agentId);
201
+ const store = new MessageStore(db);
202
+ const conversation = store.getOrCreateConversation(agentId, sessionKey, {
203
+ channelType: opts?.channelType,
204
+ channelId: opts?.channelId,
205
+ provider: opts?.provider,
206
+ model: opts?.model,
207
+ });
208
+ const neutral = userMessageToNeutral(stripMessageMetadata(content));
209
+ const stored = store.recordMessage(conversation.id, agentId, neutral, {
210
+ tokenCount: opts?.tokenCount,
211
+ isHeartbeat: opts?.isHeartbeat,
212
+ });
213
+ await this.cache.pushHistory(agentId, sessionKey, [stored], this.config.compositor.maxHistoryMessages);
214
+ await this.cache.touchSession(agentId, sessionKey);
215
+ return stored;
216
+ }
217
+ /**
218
+ * Record an assistant response.
219
+ */
220
+ async recordAssistantMessage(agentId, sessionKey, message, opts) {
221
+ const db = this.dbManager.getMessageDb(agentId);
222
+ const store = new MessageStore(db);
223
+ const conversation = store.getConversation(sessionKey);
224
+ if (!conversation) {
225
+ throw new Error(`No conversation found for session ${sessionKey}`);
226
+ }
227
+ const stored = store.recordMessage(conversation.id, agentId, message, {
228
+ tokenCount: opts?.tokenCount,
229
+ });
230
+ await this.cache.pushHistory(agentId, sessionKey, [stored], this.config.compositor.maxHistoryMessages);
231
+ await this.cache.touchSession(agentId, sessionKey);
232
+ return stored;
233
+ }
234
+ /**
235
+ * Record a raw provider response, converting to neutral format.
236
+ */
237
+ async recordProviderResponse(agentId, sessionKey, response, provider, opts) {
238
+ const neutral = fromProviderFormat(response, provider);
239
+ return this.recordAssistantMessage(agentId, sessionKey, neutral, opts);
240
+ }
241
+ /**
242
+ * Compose context for an LLM call.
243
+ */
244
+ async compose(request) {
245
+ const db = this.dbManager.getMessageDb(request.agentId);
246
+ const libraryDb = this.dbManager.getLibraryDb();
247
+ return this.compositor.compose(request, db, libraryDb);
248
+ }
249
+ /**
250
+ * Warm a session from SQLite into Redis.
251
+ */
252
+ async warm(agentId, sessionKey, opts) {
253
+ const db = this.dbManager.getMessageDb(agentId);
254
+ const libraryDb = this.dbManager.getLibraryDb();
255
+ await this.compositor.warmSession(agentId, sessionKey, db, { ...opts, libraryDb });
256
+ }
257
+ /**
258
+ * Recompute the Redis hot history view from SQLite and re-apply tool gradient.
259
+ */
260
+ async refreshRedisGradient(agentId, sessionKey, tokenBudget) {
261
+ const db = this.dbManager.getMessageDb(agentId);
262
+ await this.compositor.refreshRedisGradient(agentId, sessionKey, db, tokenBudget);
263
+ }
264
+ /**
265
+ * Full-text search across all messages for an agent.
266
+ */
267
+ search(agentId, query, limit = 20) {
268
+ const db = this.dbManager.getMessageDb(agentId);
269
+ const store = new MessageStore(db);
270
+ return store.searchMessages(agentId, query, limit);
271
+ }
272
+ /**
273
+ * Get or create a conversation.
274
+ */
275
+ getOrCreateConversation(agentId, sessionKey, opts) {
276
+ const db = this.dbManager.getMessageDb(agentId);
277
+ this.dbManager.ensureAgent(agentId);
278
+ const store = new MessageStore(db);
279
+ return store.getOrCreateConversation(agentId, sessionKey, opts);
280
+ }
281
+ /**
282
+ * List all agents with databases.
283
+ */
284
+ listAgents() {
285
+ return this.dbManager.listAgents();
286
+ }
287
+ // ─── Facts (L4: Library) ────────────────────────────────────
288
+ /**
289
+ * Add a fact.
290
+ */
291
+ addFact(agentId, content, opts) {
292
+ const db = this.dbManager.getLibraryDb();
293
+ const store = new FactStore(db);
294
+ return store.addFact(agentId, content, opts);
295
+ }
296
+ /**
297
+ * Get active facts for an agent.
298
+ */
299
+ getActiveFacts(agentId, opts) {
300
+ const db = this.dbManager.getLibraryDb();
301
+ const store = new FactStore(db);
302
+ return store.getActiveFacts(agentId, opts);
303
+ }
304
+ // ─── Knowledge (L4: Library) ────────────────────────────────
305
+ /**
306
+ * Add/update knowledge.
307
+ */
308
+ upsertKnowledge(agentId, domain, key, content, opts) {
309
+ const db = this.dbManager.getLibraryDb();
310
+ const store = new KnowledgeStore(db);
311
+ return store.upsert(agentId, domain, key, content, opts);
312
+ }
313
+ /**
314
+ * Get active knowledge, optionally filtered by domain.
315
+ */
316
+ getKnowledge(agentId, opts) {
317
+ const db = this.dbManager.getLibraryDb();
318
+ const store = new KnowledgeStore(db);
319
+ return store.getActive(agentId, opts);
320
+ }
321
+ // ─── Topics (L4: Library) ───────────────────────────────────
322
+ /**
323
+ * Create a topic.
324
+ */
325
+ createTopic(agentId, name, description) {
326
+ const db = this.dbManager.getLibraryDb();
327
+ const store = new TopicStore(db);
328
+ return store.create(agentId, name, description);
329
+ }
330
+ /**
331
+ * Get active topics.
332
+ */
333
+ getActiveTopics(agentId, limit = 20) {
334
+ const db = this.dbManager.getLibraryDb();
335
+ const store = new TopicStore(db);
336
+ return store.getActive(agentId, limit);
337
+ }
338
+ // ─── Episodes (L4: Library) ─────────────────────────────────
339
+ /**
340
+ * Record an episode.
341
+ */
342
+ recordEpisode(agentId, eventType, summary, opts) {
343
+ const db = this.dbManager.getLibraryDb();
344
+ const store = new EpisodeStore(db);
345
+ return store.record(agentId, eventType, summary, opts);
346
+ }
347
+ /**
348
+ * Get recent episodes.
349
+ */
350
+ getRecentEpisodes(agentId, opts) {
351
+ const db = this.dbManager.getLibraryDb();
352
+ const store = new EpisodeStore(db);
353
+ return store.getRecent(agentId, opts);
354
+ }
355
+ // ─── Preferences (L4: Library) ──────────────────────────────
356
+ /**
357
+ * Set a preference.
358
+ */
359
+ setPreference(subject, key, value, opts) {
360
+ const db = this.dbManager.getLibraryDb();
361
+ const store = new PreferenceStore(db);
362
+ return store.set(subject, key, value, opts);
363
+ }
364
+ /**
365
+ * Get a preference.
366
+ */
367
+ getPreference(subject, key, domain) {
368
+ const db = this.dbManager.getLibraryDb();
369
+ const store = new PreferenceStore(db);
370
+ return store.get(subject, key, domain);
371
+ }
372
+ /**
373
+ * Get all preferences for a subject.
374
+ */
375
+ getPreferences(subject, domain) {
376
+ const db = this.dbManager.getLibraryDb();
377
+ const store = new PreferenceStore(db);
378
+ return store.getForSubject(subject, domain);
379
+ }
380
+ // ─── Fleet Registry (L4: Library) ───────────────────────────
381
+ /**
382
+ * Register or update a fleet agent. Invalidates cache.
383
+ */
384
+ upsertFleetAgent(id, data) {
385
+ const db = this.dbManager.getLibraryDb();
386
+ const store = new FleetStore(db);
387
+ const result = store.upsertAgent(id, data);
388
+ // Invalidate cache — fire and forget
389
+ this.cache.invalidateFleetAgent(id).catch(() => { });
390
+ return result;
391
+ }
392
+ /**
393
+ * Get a fleet agent. Cache-aside: check Redis first, fall back to SQLite.
394
+ */
395
+ async getFleetAgentCached(id) {
396
+ // Try cache first
397
+ const cached = await this.cache.getCachedFleetAgent(id);
398
+ if (cached)
399
+ return cached;
400
+ // Fall back to SQLite
401
+ const agent = this.getFleetAgent(id);
402
+ if (agent) {
403
+ // Warm cache — fire and forget
404
+ this.cache.cacheFleetAgent(id, agent).catch(() => { });
405
+ }
406
+ return agent;
407
+ }
408
+ /**
409
+ * Get a fleet agent (synchronous, SQLite only).
410
+ */
411
+ getFleetAgent(id) {
412
+ const db = this.dbManager.getLibraryDb();
413
+ const store = new FleetStore(db);
414
+ return store.getAgent(id);
415
+ }
416
+ /**
417
+ * List fleet agents.
418
+ */
419
+ listFleetAgents(opts) {
420
+ const db = this.dbManager.getLibraryDb();
421
+ const store = new FleetStore(db);
422
+ return store.listAgents(opts);
423
+ }
424
+ /**
425
+ * Register or update a fleet org.
426
+ */
427
+ upsertFleetOrg(id, data) {
428
+ const db = this.dbManager.getLibraryDb();
429
+ const store = new FleetStore(db);
430
+ return store.upsertOrg(id, data);
431
+ }
432
+ /**
433
+ * List fleet orgs.
434
+ */
435
+ listFleetOrgs() {
436
+ const db = this.dbManager.getLibraryDb();
437
+ const store = new FleetStore(db);
438
+ return store.listOrgs();
439
+ }
440
+ // ─── Agent Capabilities (L4: Library) ────────────────────────
441
+ /**
442
+ * Register or update a capability for an agent.
443
+ */
444
+ upsertCapability(agentId, cap) {
445
+ const db = this.dbManager.getLibraryDb();
446
+ const store = new FleetStore(db);
447
+ return store.upsertCapability(agentId, cap);
448
+ }
449
+ /**
450
+ * Bulk-sync capabilities of a given type for an agent.
451
+ * Marks capabilities not in the list as 'removed'.
452
+ */
453
+ syncCapabilities(agentId, capType, caps) {
454
+ const db = this.dbManager.getLibraryDb();
455
+ const store = new FleetStore(db);
456
+ store.syncCapabilities(agentId, capType, caps);
457
+ }
458
+ /**
459
+ * Get capabilities for an agent, optionally filtered by type.
460
+ */
461
+ getAgentCapabilities(agentId, capType) {
462
+ const db = this.dbManager.getLibraryDb();
463
+ const store = new FleetStore(db);
464
+ return store.getAgentCapabilities(agentId, capType);
465
+ }
466
+ /**
467
+ * Find agents that have a specific capability.
468
+ */
469
+ findAgentsByCapability(capType, name) {
470
+ const db = this.dbManager.getLibraryDb();
471
+ const store = new FleetStore(db);
472
+ return store.findByCapability(capType, name);
473
+ }
474
+ // ─── System Registry (L4: Library) ──────────────────────────
475
+ /**
476
+ * Set a system state value.
477
+ */
478
+ setSystemState(category, key, value, opts) {
479
+ const db = this.dbManager.getLibraryDb();
480
+ const store = new SystemStore(db);
481
+ return store.set(category, key, value, opts);
482
+ }
483
+ /**
484
+ * Get a system state value.
485
+ */
486
+ getSystemState(category, key) {
487
+ const db = this.dbManager.getLibraryDb();
488
+ const store = new SystemStore(db);
489
+ return store.get(category, key);
490
+ }
491
+ /**
492
+ * Get all state in a category.
493
+ */
494
+ getSystemCategory(category) {
495
+ const db = this.dbManager.getLibraryDb();
496
+ const store = new SystemStore(db);
497
+ return store.getCategory(category);
498
+ }
499
+ // ─── Work Items (L4: Library) ───────────────────────────────
500
+ /**
501
+ * Create a work item.
502
+ */
503
+ createWorkItem(data) {
504
+ const db = this.dbManager.getLibraryDb();
505
+ const store = new WorkStore(db);
506
+ return store.create(data);
507
+ }
508
+ /**
509
+ * Update work item status.
510
+ */
511
+ updateWorkStatus(id, status, agentId, comment) {
512
+ const db = this.dbManager.getLibraryDb();
513
+ const store = new WorkStore(db);
514
+ return store.updateStatus(id, status, agentId, comment);
515
+ }
516
+ /**
517
+ * Get active work for an agent.
518
+ */
519
+ getAgentWork(agentId, status) {
520
+ const db = this.dbManager.getLibraryDb();
521
+ const store = new WorkStore(db);
522
+ return store.getAgentWork(agentId, status);
523
+ }
524
+ /**
525
+ * Get the fleet kanban board.
526
+ */
527
+ getFleetKanban(opts) {
528
+ const db = this.dbManager.getLibraryDb();
529
+ const store = new WorkStore(db);
530
+ return store.getKanban(opts);
531
+ }
532
+ /**
533
+ * Get work item stats.
534
+ */
535
+ getWorkStats(opts) {
536
+ const db = this.dbManager.getLibraryDb();
537
+ const store = new WorkStore(db);
538
+ return store.getStats(opts);
539
+ }
540
+ /**
541
+ * Get blocked work items.
542
+ */
543
+ getBlockedWork() {
544
+ const db = this.dbManager.getLibraryDb();
545
+ const store = new WorkStore(db);
546
+ return store.getBlocked();
547
+ }
548
+ // ─── Agent Desired State (L4: Library) ──────────────────────
549
+ /**
550
+ * Set desired configuration for an agent.
551
+ */
552
+ setDesiredState(agentId, configKey, desiredValue, opts) {
553
+ const db = this.dbManager.getLibraryDb();
554
+ const store = new DesiredStateStore(db);
555
+ const result = store.setDesired(agentId, configKey, desiredValue, opts);
556
+ // Invalidate cache — desired state change affects fleet view
557
+ this.cache.invalidateFleetAgent(agentId).catch(() => { });
558
+ return result;
559
+ }
560
+ /**
561
+ * Report actual runtime value for drift detection. Invalidates cache.
562
+ */
563
+ reportActualState(agentId, configKey, actualValue) {
564
+ const db = this.dbManager.getLibraryDb();
565
+ const store = new DesiredStateStore(db);
566
+ const result = store.reportActual(agentId, configKey, actualValue);
567
+ this.cache.invalidateFleetAgent(agentId).catch(() => { });
568
+ return result;
569
+ }
570
+ /**
571
+ * Bulk report actual state (e.g., on session startup / heartbeat). Invalidates cache.
572
+ */
573
+ reportActualStateBulk(agentId, actuals) {
574
+ const db = this.dbManager.getLibraryDb();
575
+ const store = new DesiredStateStore(db);
576
+ const result = store.reportActualBulk(agentId, actuals);
577
+ this.cache.invalidateFleetAgent(agentId).catch(() => { });
578
+ return result;
579
+ }
580
+ /**
581
+ * Get all desired state for an agent.
582
+ */
583
+ getDesiredState(agentId) {
584
+ const db = this.dbManager.getLibraryDb();
585
+ const store = new DesiredStateStore(db);
586
+ return store.getAgentState(agentId);
587
+ }
588
+ /**
589
+ * Get desired state as a flat config map.
590
+ */
591
+ getDesiredConfig(agentId) {
592
+ const db = this.dbManager.getLibraryDb();
593
+ const store = new DesiredStateStore(db);
594
+ return store.getAgentConfig(agentId);
595
+ }
596
+ /**
597
+ * Get all drifted entries across the fleet.
598
+ */
599
+ getDriftedState() {
600
+ const db = this.dbManager.getLibraryDb();
601
+ const store = new DesiredStateStore(db);
602
+ return store.getDrifted();
603
+ }
604
+ /**
605
+ * Get fleet-wide view of a specific config key.
606
+ */
607
+ getFleetConfigKey(configKey) {
608
+ const db = this.dbManager.getLibraryDb();
609
+ const store = new DesiredStateStore(db);
610
+ return store.getFleetConfig(configKey);
611
+ }
612
+ /**
613
+ * Get config change history.
614
+ */
615
+ getConfigHistory(agentId, configKey, limit) {
616
+ const db = this.dbManager.getLibraryDb();
617
+ const store = new DesiredStateStore(db);
618
+ return store.getHistory(agentId, configKey, limit);
619
+ }
620
+ /**
621
+ * Get fleet drift summary.
622
+ */
623
+ getDriftSummary() {
624
+ const db = this.dbManager.getLibraryDb();
625
+ const store = new DesiredStateStore(db);
626
+ return store.getDriftSummary();
627
+ }
628
+ // ─── Knowledge Graph (L4: Library) ──────────────────────────
629
+ /**
630
+ * Add a directed link between two entities.
631
+ */
632
+ addKnowledgeLink(fromType, fromId, toType, toId, linkType) {
633
+ const db = this.dbManager.getLibraryDb();
634
+ const graph = new KnowledgeGraph(db);
635
+ return graph.addLink(fromType, fromId, toType, toId, linkType);
636
+ }
637
+ /**
638
+ * Remove a specific link.
639
+ */
640
+ removeKnowledgeLink(fromType, fromId, toType, toId, linkType) {
641
+ const db = this.dbManager.getLibraryDb();
642
+ const graph = new KnowledgeGraph(db);
643
+ return graph.removeLink(fromType, fromId, toType, toId, linkType);
644
+ }
645
+ /**
646
+ * Get all links for an entity (both directions).
647
+ */
648
+ getEntityLinks(type, id) {
649
+ const db = this.dbManager.getLibraryDb();
650
+ const graph = new KnowledgeGraph(db);
651
+ return graph.getLinks(type, id);
652
+ }
653
+ /**
654
+ * Traverse the knowledge graph from a starting entity.
655
+ * BFS with bounded depth and result count.
656
+ */
657
+ traverseGraph(startType, startId, opts) {
658
+ const db = this.dbManager.getLibraryDb();
659
+ const graph = new KnowledgeGraph(db);
660
+ return graph.traverse(startType, startId, opts);
661
+ }
662
+ /**
663
+ * Find the shortest path between two entities.
664
+ */
665
+ findGraphPath(fromType, fromId, toType, toId, maxDepth) {
666
+ const db = this.dbManager.getLibraryDb();
667
+ const graph = new KnowledgeGraph(db);
668
+ return graph.findPath(fromType, fromId, toType, toId, maxDepth);
669
+ }
670
+ /**
671
+ * Get the most connected entities.
672
+ */
673
+ getMostConnectedEntities(opts) {
674
+ const db = this.dbManager.getLibraryDb();
675
+ const graph = new KnowledgeGraph(db);
676
+ return graph.getMostConnected(opts);
677
+ }
678
+ /**
679
+ * Get knowledge graph statistics.
680
+ */
681
+ getGraphStats() {
682
+ const db = this.dbManager.getLibraryDb();
683
+ const graph = new KnowledgeGraph(db);
684
+ return {
685
+ totalLinks: graph.getTotalLinks(),
686
+ byType: graph.getLinkStats(),
687
+ };
688
+ }
689
+ // ─── Vector / Semantic Search (L3: Vectors DB) ──────────────
690
+ /**
691
+ * Semantic search across an agent's indexed memory.
692
+ */
693
+ async semanticSearch(agentId, query, opts) {
694
+ const db = this.dbManager.getVectorDb(agentId);
695
+ if (!db) {
696
+ console.warn('[hypermem] Semantic search unavailable — sqlite-vec not loaded');
697
+ return [];
698
+ }
699
+ const libraryDb = this.dbManager.getLibraryDb();
700
+ const vs = new VectorStore(db, this.config.embedding, libraryDb);
701
+ return vs.search(query, opts);
702
+ }
703
+ /**
704
+ * Index all un-indexed content for an agent.
705
+ */
706
+ async indexAgent(agentId) {
707
+ const db = this.dbManager.getVectorDb(agentId);
708
+ if (!db)
709
+ return { indexed: 0, skipped: 0, tombstoned: 0 };
710
+ const libraryDb = this.dbManager.getLibraryDb();
711
+ const vs = new VectorStore(db, this.config.embedding, libraryDb);
712
+ vs.ensureTables();
713
+ const result = await vs.indexAll(agentId);
714
+ // Tombstone superseded facts/knowledge so they don't surface in recall
715
+ const tombstoned = vs.tombstoneSuperseded();
716
+ return { ...result, tombstoned };
717
+ }
718
+ /**
719
+ * Get vector index statistics.
720
+ */
721
+ getVectorStats(agentId) {
722
+ const db = this.dbManager.getVectorDb(agentId);
723
+ if (!db)
724
+ return null;
725
+ const libraryDb = this.dbManager.getLibraryDb();
726
+ const vs = new VectorStore(db, this.config.embedding, libraryDb);
727
+ return vs.getStats();
728
+ }
729
+ /**
730
+ * Prune orphaned vector entries.
731
+ */
732
+ pruneVectorOrphans(agentId) {
733
+ const db = this.dbManager.getVectorDb(agentId);
734
+ if (!db)
735
+ return 0;
736
+ const libraryDb = this.dbManager.getLibraryDb();
737
+ const vs = new VectorStore(db, this.config.embedding, libraryDb);
738
+ return vs.pruneOrphans();
739
+ }
740
+ // ─── Session Cursor (dual-read: Redis → SQLite fallback) ──────
741
+ /**
742
+ * Get the session cursor for an agent+session.
743
+ * Reads from Redis first; falls back to SQLite if Redis returns null
744
+ * (e.g. after eviction or restart). This is the P1.3 durability guarantee.
745
+ */
746
+ async getSessionCursor(agentId, sessionKey) {
747
+ // Try Redis first (hot path)
748
+ const redisCursor = await this.cache.getCursor(agentId, sessionKey);
749
+ if (redisCursor)
750
+ return redisCursor;
751
+ // Fallback to SQLite
752
+ const db = this.dbManager.getMessageDb(agentId);
753
+ if (!db)
754
+ return null;
755
+ const row = db.prepare(`
756
+ SELECT cursor_last_sent_id, cursor_last_sent_index, cursor_last_sent_at,
757
+ cursor_window_size, cursor_token_count
758
+ FROM conversations
759
+ WHERE session_key = ? AND cursor_last_sent_id IS NOT NULL
760
+ `).get(sessionKey);
761
+ if (!row || row.cursor_last_sent_id == null)
762
+ return null;
763
+ const cursor = {
764
+ lastSentId: row.cursor_last_sent_id,
765
+ lastSentIndex: row.cursor_last_sent_index,
766
+ lastSentAt: row.cursor_last_sent_at,
767
+ windowSize: row.cursor_window_size,
768
+ tokenCount: row.cursor_token_count,
769
+ };
770
+ // Re-warm Redis so subsequent reads are fast
771
+ try {
772
+ await this.cache.setCursor(agentId, sessionKey, cursor);
773
+ }
774
+ catch {
775
+ // Best-effort re-warm
776
+ }
777
+ return cursor;
778
+ }
779
+ // ─── Message Rotation (L2: Messages) ────────────────────────
780
+ /**
781
+ * Get the size of an agent's active messages.db in bytes.
782
+ */
783
+ getMessageDbSize(agentId) {
784
+ return this.dbManager.getMessageDbSize(agentId);
785
+ }
786
+ /**
787
+ * Check if an agent's message database needs rotation.
788
+ */
789
+ shouldRotate(agentId, opts) {
790
+ return this.dbManager.shouldRotate(agentId, opts);
791
+ }
792
+ /**
793
+ * Rotate an agent's message database.
794
+ * Returns the path to the rotated file, or null if no active DB exists.
795
+ */
796
+ rotateMessageDb(agentId) {
797
+ return this.dbManager.rotateMessageDb(agentId);
798
+ }
799
+ /**
800
+ * List rotated message DB files for an agent.
801
+ */
802
+ listRotatedDbs(agentId) {
803
+ return this.dbManager.listRotatedDbs(agentId);
804
+ }
805
+ /**
806
+ * Check and auto-rotate all agents' message databases.
807
+ * Call on heartbeat/startup.
808
+ * Returns agents that were rotated.
809
+ */
810
+ autoRotate(opts) {
811
+ const agents = this.dbManager.listAgents();
812
+ const rotated = [];
813
+ for (const agentId of agents) {
814
+ const check = this.shouldRotate(agentId, opts);
815
+ if (check) {
816
+ const rotatedPath = this.rotateMessageDb(agentId);
817
+ if (rotatedPath) {
818
+ rotated.push({
819
+ agentId,
820
+ reason: `${check.reason}: ${check.current} > ${check.threshold}`,
821
+ rotatedTo: rotatedPath,
822
+ });
823
+ }
824
+ }
825
+ }
826
+ return rotated;
827
+ }
828
+ // ─── Session Registry (L4: Library) ─────────────────────────
829
+ /**
830
+ * Register a session start.
831
+ */
832
+ registerSession(sessionKey, agentId, opts) {
833
+ const db = this.dbManager.getLibraryDb();
834
+ const now = new Date().toISOString();
835
+ const existing = db
836
+ .prepare('SELECT id FROM session_registry WHERE id = ?')
837
+ .get(sessionKey);
838
+ if (existing) {
839
+ db.prepare('UPDATE session_registry SET status = ?, started_at = ? WHERE id = ?')
840
+ .run('active', now, sessionKey);
841
+ }
842
+ else {
843
+ db.prepare(`INSERT INTO session_registry (id, agent_id, channel, channel_type, started_at, status)
844
+ VALUES (?, ?, ?, ?, ?, 'active')`).run(sessionKey, agentId, opts?.channel || null, opts?.channelType || null, now);
845
+ }
846
+ db.prepare('INSERT INTO session_events (session_id, event_type, timestamp, payload) VALUES (?, ?, ?, ?)').run(sessionKey, 'start', now, JSON.stringify({ channel: opts?.channel, channelType: opts?.channelType }));
847
+ }
848
+ /**
849
+ * Record a session event.
850
+ */
851
+ recordSessionEvent(sessionKey, eventType, payload) {
852
+ const db = this.dbManager.getLibraryDb();
853
+ db.prepare('INSERT INTO session_events (session_id, event_type, timestamp, payload) VALUES (?, ?, ?, ?)').run(sessionKey, eventType, new Date().toISOString(), payload ? JSON.stringify(payload) : null);
854
+ if (eventType === 'decision') {
855
+ db.prepare('UPDATE session_registry SET decisions_made = decisions_made + 1 WHERE id = ?').run(sessionKey);
856
+ }
857
+ else if (eventType === 'fact_extracted') {
858
+ db.prepare('UPDATE session_registry SET facts_extracted = facts_extracted + 1 WHERE id = ?').run(sessionKey);
859
+ }
860
+ }
861
+ /**
862
+ * Close a session.
863
+ */
864
+ closeSession(sessionKey, summary) {
865
+ const db = this.dbManager.getLibraryDb();
866
+ const now = new Date().toISOString();
867
+ db.prepare('UPDATE session_registry SET status = ?, ended_at = ?, summary = ? WHERE id = ?').run('completed', now, summary || null, sessionKey);
868
+ db.prepare('INSERT INTO session_events (session_id, event_type, timestamp) VALUES (?, ?, ?)').run(sessionKey, 'completion', now);
869
+ }
870
+ /**
871
+ * Query sessions.
872
+ */
873
+ querySessions(opts) {
874
+ const db = this.dbManager.getLibraryDb();
875
+ const conditions = [];
876
+ const params = [];
877
+ if (opts?.agentId) {
878
+ conditions.push('agent_id = ?');
879
+ params.push(opts.agentId);
880
+ }
881
+ if (opts?.status) {
882
+ conditions.push('status = ?');
883
+ params.push(opts.status);
884
+ }
885
+ if (opts?.since) {
886
+ conditions.push('started_at >= ?');
887
+ params.push(opts.since);
888
+ }
889
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
890
+ return db
891
+ .prepare(`SELECT * FROM session_registry ${where} ORDER BY started_at DESC LIMIT ?`)
892
+ .all(...params, opts?.limit || 50);
893
+ }
894
+ /**
895
+ * Get session events.
896
+ */
897
+ getSessionEvents(sessionKey, limit = 50) {
898
+ const db = this.dbManager.getLibraryDb();
899
+ return db
900
+ .prepare('SELECT * FROM session_events WHERE session_id = ? ORDER BY timestamp DESC LIMIT ?')
901
+ .all(sessionKey, limit);
902
+ }
903
+ // ─── Cross-Agent Queries ─────────────────────────────────────
904
+ /**
905
+ * Query another agent's memory with visibility-scoped access.
906
+ */
907
+ queryAgent(requesterId, targetAgentId, opts, registry) {
908
+ return crossAgentQuery(this.dbManager, {
909
+ requesterId,
910
+ targetAgentId,
911
+ memoryType: opts?.memoryType || 'facts',
912
+ domain: opts?.domain,
913
+ limit: opts?.limit,
914
+ }, registry || buildOrgRegistryFromDb(this.dbManager.getLibraryDb()));
915
+ }
916
+ /**
917
+ * Query fleet-wide visible memory.
918
+ */
919
+ queryFleet(requesterId, opts, registry) {
920
+ const reg = registry || buildOrgRegistryFromDb(this.dbManager.getLibraryDb());
921
+ const results = [];
922
+ // Query all agents from the fleet registry
923
+ const libraryDb = this.dbManager.getLibraryDb();
924
+ const agents = libraryDb
925
+ .prepare("SELECT id FROM fleet_agents WHERE status = 'active'")
926
+ .all();
927
+ for (const agent of agents) {
928
+ if (agent.id === requesterId)
929
+ continue;
930
+ try {
931
+ const agentResults = this.queryAgent(requesterId, agent.id, opts, reg);
932
+ results.push(...agentResults);
933
+ }
934
+ catch {
935
+ // Skip agents we can't query (not in registry)
936
+ }
937
+ }
938
+ return results;
939
+ }
940
+ // ─── Document Chunks (L4: Library) ──────────────────────────
941
+ /**
942
+ * Index chunks from a parsed set of DocChunk objects.
943
+ * Atomic: replaces all chunks for the source in one transaction.
944
+ */
945
+ indexDocChunks(chunks) {
946
+ const db = this.dbManager.getLibraryDb();
947
+ const store = new DocChunkStore(db);
948
+ return store.indexChunks(chunks);
949
+ }
950
+ /**
951
+ * Query doc chunks by collection with optional keyword/scope/agent filters.
952
+ */
953
+ queryDocChunks(query) {
954
+ const db = this.dbManager.getLibraryDb();
955
+ const store = new DocChunkStore(db);
956
+ return store.queryChunks(query);
957
+ }
958
+ /**
959
+ * Seed all ACA files from a workspace directory into the doc chunk index.
960
+ * Idempotent: skips files whose source hash hasn't changed.
961
+ * Force re-index with opts.force = true.
962
+ */
963
+ async seedWorkspace(workspaceDir, opts = {}) {
964
+ const db = this.dbManager.getLibraryDb();
965
+ const seeder = new WorkspaceSeeder(db);
966
+ return seeder.seedWorkspace(workspaceDir, opts);
967
+ }
968
+ /**
969
+ * Seed a single file into the doc chunk index.
970
+ */
971
+ seedFile(filePath, collection, opts = {}) {
972
+ const db = this.dbManager.getLibraryDb();
973
+ const seeder = new WorkspaceSeeder(db);
974
+ return seeder.seedFile(filePath, collection, opts);
975
+ }
976
+ /**
977
+ * Get stats about the current doc chunk index.
978
+ */
979
+ getDocIndexStats() {
980
+ const db = this.dbManager.getLibraryDb();
981
+ const store = new DocChunkStore(db);
982
+ return store.getStats();
983
+ }
984
+ /**
985
+ * List indexed sources (what files have been seeded and their hashes).
986
+ */
987
+ listDocSources(opts) {
988
+ const db = this.dbManager.getLibraryDb();
989
+ const store = new DocChunkStore(db);
990
+ return store.listSources(opts);
991
+ }
992
+ // ─── Fleet Cache Hydration ──────────────────────────────────
993
+ /**
994
+ * Hydrate the Redis fleet cache from library.db.
995
+ * Call on gateway startup to warm the cache for dashboard queries.
996
+ *
997
+ * Populates:
998
+ * - Per-agent profiles (fleet registry + capabilities + desired state)
999
+ * - Fleet summary (counts, drift status)
1000
+ */
1001
+ async hydrateFleetCache() {
1002
+ if (!this.cache.isConnected)
1003
+ return { agents: 0, summary: false };
1004
+ const db = this.dbManager.getLibraryDb();
1005
+ const fleetStore = new FleetStore(db);
1006
+ const desiredStore = new DesiredStateStore(db);
1007
+ const agents = fleetStore.listAgents();
1008
+ let hydrated = 0;
1009
+ for (const agent of agents) {
1010
+ try {
1011
+ // Build a composite profile for each agent
1012
+ const capabilities = fleetStore.getAgentCapabilities(agent.id);
1013
+ const desiredState = desiredStore.getAgentState(agent.id);
1014
+ const desiredConfig = desiredStore.getAgentConfig(agent.id);
1015
+ const composite = {
1016
+ ...agent,
1017
+ capabilities: capabilities.map(c => ({ capType: c.capType, name: c.name, version: c.version })),
1018
+ desiredState: desiredState.map(d => ({
1019
+ configKey: d.configKey,
1020
+ desiredValue: d.desiredValue,
1021
+ actualValue: d.actualValue,
1022
+ driftStatus: d.driftStatus,
1023
+ })),
1024
+ desiredConfig,
1025
+ };
1026
+ await this.cache.cacheFleetAgent(agent.id, composite);
1027
+ hydrated++;
1028
+ }
1029
+ catch (err) {
1030
+ const message = err instanceof Error ? err.message : String(err);
1031
+ console.warn(`[hypermem] Failed to cache agent ${agent.id}: ${message}`);
1032
+ }
1033
+ }
1034
+ // Cache fleet summary
1035
+ try {
1036
+ const driftSummary = desiredStore.getDriftSummary();
1037
+ const summary = {
1038
+ totalAgents: agents.length,
1039
+ activeAgents: agents.filter(a => a.status === 'active').length,
1040
+ tiers: {
1041
+ council: agents.filter(a => a.tier === 'council').length,
1042
+ director: agents.filter(a => a.tier === 'director').length,
1043
+ specialist: agents.filter(a => a.tier === 'specialist').length,
1044
+ },
1045
+ drift: driftSummary,
1046
+ hydratedAt: new Date().toISOString(),
1047
+ };
1048
+ await this.cache.cacheFleetSummary(summary);
1049
+ }
1050
+ catch {
1051
+ return { agents: hydrated, summary: false };
1052
+ }
1053
+ return { agents: hydrated, summary: true };
1054
+ }
1055
+ // ─── Lifecycle ───────────────────────────────────────────────
1056
+ /**
1057
+ * Clean shutdown.
1058
+ */
1059
+ async close() {
1060
+ await this.cache.disconnect();
1061
+ this.dbManager.close();
1062
+ }
1063
+ }
1064
+ export default HyperMem;
1065
+ export { SessionFlusher, flushSession } from './session-flusher.js';
1066
+ export { importVault, watchVault, parseObsidianNote, parseFrontmatter, extractWikilinks, extractTags, cleanObsidianMarkdown } from './obsidian-watcher.js';
1067
+ export { exportToVault } from './obsidian-exporter.js';
1068
+ export { collectMetrics, formatMetricsSummary } from './metrics-dashboard.js';
1069
+ export { getProfile, mergeProfile, PROFILES, lightProfile, standardProfile, fullProfile, extendedProfile, minimalProfile, richProfile } from './profiles.js';
1070
+ export { renderStarterFOS, resolveOutputTier } from './fos-mod.js';
1071
+ export { repairToolPairs } from './repair-tool-pairs.js';
1072
+ //# sourceMappingURL=index.js.map