@psiclawops/hypermem 0.5.0 → 0.5.2

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 (163) hide show
  1. package/ARCHITECTURE.md +12 -3
  2. package/README.md +30 -6
  3. package/bin/hypermem-status.mjs +166 -0
  4. package/dist/background-indexer.d.ts +132 -0
  5. package/dist/background-indexer.d.ts.map +1 -0
  6. package/dist/background-indexer.js +1044 -0
  7. package/dist/cache.d.ts +110 -0
  8. package/dist/cache.d.ts.map +1 -0
  9. package/dist/cache.js +495 -0
  10. package/dist/compaction-fence.d.ts +89 -0
  11. package/dist/compaction-fence.d.ts.map +1 -0
  12. package/dist/compaction-fence.js +153 -0
  13. package/dist/compositor.d.ts +226 -0
  14. package/dist/compositor.d.ts.map +1 -0
  15. package/dist/compositor.js +2558 -0
  16. package/dist/content-type-classifier.d.ts +41 -0
  17. package/dist/content-type-classifier.d.ts.map +1 -0
  18. package/dist/content-type-classifier.js +181 -0
  19. package/dist/cross-agent.d.ts +62 -0
  20. package/dist/cross-agent.d.ts.map +1 -0
  21. package/dist/cross-agent.js +259 -0
  22. package/dist/db.d.ts +131 -0
  23. package/dist/db.d.ts.map +1 -0
  24. package/dist/db.js +402 -0
  25. package/dist/desired-state-store.d.ts +100 -0
  26. package/dist/desired-state-store.d.ts.map +1 -0
  27. package/dist/desired-state-store.js +222 -0
  28. package/dist/doc-chunk-store.d.ts +140 -0
  29. package/dist/doc-chunk-store.d.ts.map +1 -0
  30. package/dist/doc-chunk-store.js +391 -0
  31. package/dist/doc-chunker.d.ts +99 -0
  32. package/dist/doc-chunker.d.ts.map +1 -0
  33. package/dist/doc-chunker.js +324 -0
  34. package/dist/dreaming-promoter.d.ts +86 -0
  35. package/dist/dreaming-promoter.d.ts.map +1 -0
  36. package/dist/dreaming-promoter.js +381 -0
  37. package/dist/episode-store.d.ts +49 -0
  38. package/dist/episode-store.d.ts.map +1 -0
  39. package/dist/episode-store.js +135 -0
  40. package/dist/fact-store.d.ts +75 -0
  41. package/dist/fact-store.d.ts.map +1 -0
  42. package/dist/fact-store.js +236 -0
  43. package/dist/fleet-store.d.ts +144 -0
  44. package/dist/fleet-store.d.ts.map +1 -0
  45. package/dist/fleet-store.js +276 -0
  46. package/dist/fos-mod.d.ts +178 -0
  47. package/dist/fos-mod.d.ts.map +1 -0
  48. package/dist/fos-mod.js +416 -0
  49. package/dist/hybrid-retrieval.d.ts +64 -0
  50. package/dist/hybrid-retrieval.d.ts.map +1 -0
  51. package/dist/hybrid-retrieval.js +344 -0
  52. package/dist/image-eviction.d.ts +49 -0
  53. package/dist/image-eviction.d.ts.map +1 -0
  54. package/dist/image-eviction.js +251 -0
  55. package/dist/index.d.ts +650 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +1072 -0
  58. package/dist/keystone-scorer.d.ts +51 -0
  59. package/dist/keystone-scorer.d.ts.map +1 -0
  60. package/dist/keystone-scorer.js +52 -0
  61. package/dist/knowledge-graph.d.ts +110 -0
  62. package/dist/knowledge-graph.d.ts.map +1 -0
  63. package/dist/knowledge-graph.js +305 -0
  64. package/dist/knowledge-lint.d.ts +29 -0
  65. package/dist/knowledge-lint.d.ts.map +1 -0
  66. package/dist/knowledge-lint.js +116 -0
  67. package/dist/knowledge-store.d.ts +72 -0
  68. package/dist/knowledge-store.d.ts.map +1 -0
  69. package/dist/knowledge-store.js +247 -0
  70. package/dist/library-schema.d.ts +22 -0
  71. package/dist/library-schema.d.ts.map +1 -0
  72. package/dist/library-schema.js +1038 -0
  73. package/dist/message-store.d.ts +89 -0
  74. package/dist/message-store.d.ts.map +1 -0
  75. package/dist/message-store.js +323 -0
  76. package/dist/metrics-dashboard.d.ts +114 -0
  77. package/dist/metrics-dashboard.d.ts.map +1 -0
  78. package/dist/metrics-dashboard.js +260 -0
  79. package/dist/obsidian-exporter.d.ts +57 -0
  80. package/dist/obsidian-exporter.d.ts.map +1 -0
  81. package/dist/obsidian-exporter.js +274 -0
  82. package/dist/obsidian-watcher.d.ts +147 -0
  83. package/dist/obsidian-watcher.d.ts.map +1 -0
  84. package/dist/obsidian-watcher.js +403 -0
  85. package/dist/open-domain.d.ts +46 -0
  86. package/dist/open-domain.d.ts.map +1 -0
  87. package/dist/open-domain.js +125 -0
  88. package/dist/preference-store.d.ts +54 -0
  89. package/dist/preference-store.d.ts.map +1 -0
  90. package/dist/preference-store.js +109 -0
  91. package/dist/preservation-gate.d.ts +82 -0
  92. package/dist/preservation-gate.d.ts.map +1 -0
  93. package/dist/preservation-gate.js +150 -0
  94. package/dist/proactive-pass.d.ts +63 -0
  95. package/dist/proactive-pass.d.ts.map +1 -0
  96. package/dist/proactive-pass.js +239 -0
  97. package/dist/profiles.d.ts +44 -0
  98. package/dist/profiles.d.ts.map +1 -0
  99. package/dist/profiles.js +227 -0
  100. package/dist/provider-translator.d.ts +50 -0
  101. package/dist/provider-translator.d.ts.map +1 -0
  102. package/dist/provider-translator.js +403 -0
  103. package/dist/rate-limiter.d.ts +76 -0
  104. package/dist/rate-limiter.d.ts.map +1 -0
  105. package/dist/rate-limiter.js +179 -0
  106. package/dist/repair-tool-pairs.d.ts +38 -0
  107. package/dist/repair-tool-pairs.d.ts.map +1 -0
  108. package/dist/repair-tool-pairs.js +138 -0
  109. package/dist/retrieval-policy.d.ts +51 -0
  110. package/dist/retrieval-policy.d.ts.map +1 -0
  111. package/dist/retrieval-policy.js +77 -0
  112. package/dist/schema.d.ts +15 -0
  113. package/dist/schema.d.ts.map +1 -0
  114. package/dist/schema.js +229 -0
  115. package/dist/secret-scanner.d.ts +51 -0
  116. package/dist/secret-scanner.d.ts.map +1 -0
  117. package/dist/secret-scanner.js +248 -0
  118. package/dist/seed.d.ts +108 -0
  119. package/dist/seed.d.ts.map +1 -0
  120. package/dist/seed.js +177 -0
  121. package/dist/session-flusher.d.ts +53 -0
  122. package/dist/session-flusher.d.ts.map +1 -0
  123. package/dist/session-flusher.js +69 -0
  124. package/dist/session-topic-map.d.ts +41 -0
  125. package/dist/session-topic-map.d.ts.map +1 -0
  126. package/dist/session-topic-map.js +77 -0
  127. package/dist/spawn-context.d.ts +54 -0
  128. package/dist/spawn-context.d.ts.map +1 -0
  129. package/dist/spawn-context.js +159 -0
  130. package/dist/system-store.d.ts +73 -0
  131. package/dist/system-store.d.ts.map +1 -0
  132. package/dist/system-store.js +182 -0
  133. package/dist/temporal-store.d.ts +80 -0
  134. package/dist/temporal-store.d.ts.map +1 -0
  135. package/dist/temporal-store.js +149 -0
  136. package/dist/topic-detector.d.ts +35 -0
  137. package/dist/topic-detector.d.ts.map +1 -0
  138. package/dist/topic-detector.js +249 -0
  139. package/dist/topic-store.d.ts +45 -0
  140. package/dist/topic-store.d.ts.map +1 -0
  141. package/dist/topic-store.js +136 -0
  142. package/dist/topic-synthesizer.d.ts +51 -0
  143. package/dist/topic-synthesizer.d.ts.map +1 -0
  144. package/dist/topic-synthesizer.js +315 -0
  145. package/dist/trigger-registry.d.ts +63 -0
  146. package/dist/trigger-registry.d.ts.map +1 -0
  147. package/dist/trigger-registry.js +163 -0
  148. package/dist/types.d.ts +537 -0
  149. package/dist/types.d.ts.map +1 -0
  150. package/dist/types.js +9 -0
  151. package/dist/vector-store.d.ts +170 -0
  152. package/dist/vector-store.d.ts.map +1 -0
  153. package/dist/vector-store.js +677 -0
  154. package/dist/version.d.ts +34 -0
  155. package/dist/version.d.ts.map +1 -0
  156. package/dist/version.js +34 -0
  157. package/dist/wiki-page-emitter.d.ts +65 -0
  158. package/dist/wiki-page-emitter.d.ts.map +1 -0
  159. package/dist/wiki-page-emitter.js +258 -0
  160. package/dist/work-store.d.ts +112 -0
  161. package/dist/work-store.d.ts.map +1 -0
  162. package/dist/work-store.js +273 -0
  163. package/package.json +4 -1
@@ -0,0 +1,236 @@
1
+ /**
2
+ * hypermem Fact Store
3
+ *
4
+ * CRUD operations for facts (extracted knowledge that spans sessions).
5
+ * Facts live in the central library DB, tagged by agent_id.
6
+ * Facts have scope (agent/session/user), confidence, and decay.
7
+ */
8
+ import { isSafeForSharedVisibility, requiresScan } from './secret-scanner.js';
9
+ function nowIso() {
10
+ return new Date().toISOString();
11
+ }
12
+ function parseFactRow(row) {
13
+ return {
14
+ id: row.id,
15
+ agentId: row.agent_id,
16
+ scope: row.scope,
17
+ domain: row.domain || null,
18
+ content: row.content,
19
+ confidence: row.confidence,
20
+ visibility: row.visibility || 'private',
21
+ sourceType: row.source_type || 'conversation',
22
+ sourceSessionKey: row.source_session_key || null,
23
+ sourceRef: row.source_ref || null,
24
+ createdAt: row.created_at,
25
+ updatedAt: row.updated_at,
26
+ expiresAt: row.expires_at || null,
27
+ supersededBy: row.superseded_by || null,
28
+ decayScore: row.decay_score,
29
+ };
30
+ }
31
+ export class FactStore {
32
+ db;
33
+ constructor(db) {
34
+ this.db = db;
35
+ }
36
+ /**
37
+ * Add a new fact. Checks for duplicates by content.
38
+ */
39
+ addFact(agentId, content, opts) {
40
+ const now = nowIso();
41
+ const scope = opts?.scope || 'agent';
42
+ // KL-01: global scope is not yet supported — write gate is deferred to 1.0.
43
+ // Log a warning if a caller somehow passes scope='global' (e.g. direct DB
44
+ // access bypassing TypeScript types or a future FactScope addition).
45
+ if (scope === 'global') {
46
+ console.warn(`[hypermem] WARNING: agent '${agentId}' attempted to write a fact with scope='global'. ` +
47
+ `Global-scope facts are not yet gated — this write will succeed but may propagate ` +
48
+ `to all agents sharing library.db. See KL-01 in KNOWN_LIMITATIONS.md.`);
49
+ }
50
+ // Secret gate: if requested visibility is shared, verify content is clean.
51
+ // Downgrade to 'private' rather than reject — matches episode-store pattern.
52
+ let resolvedVisibility = opts?.visibility || 'private';
53
+ if (requiresScan(resolvedVisibility) && !isSafeForSharedVisibility(content)) {
54
+ resolvedVisibility = 'private';
55
+ }
56
+ // Check for exact duplicate
57
+ const existing = this.db.prepare(`
58
+ SELECT * FROM facts WHERE agent_id = ? AND content = ? AND scope = ?
59
+ `).get(agentId, content, scope);
60
+ if (existing) {
61
+ this.db.prepare(`
62
+ UPDATE facts SET confidence = MAX(confidence, ?), updated_at = ? WHERE id = ?
63
+ `).run(opts?.confidence || 1.0, now, existing.id);
64
+ return parseFactRow({ ...existing, updated_at: now });
65
+ }
66
+ const result = this.db.prepare(`
67
+ INSERT INTO facts (agent_id, scope, domain, content, confidence,
68
+ visibility, source_type, source_session_key, source_ref,
69
+ created_at, updated_at, expires_at, decay_score)
70
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0.0)
71
+ `).run(agentId, scope, opts?.domain || null, content, opts?.confidence || 1.0, resolvedVisibility, opts?.sourceType || 'conversation', opts?.sourceSessionKey || null, opts?.sourceRef || null, now, now, opts?.expiresAt || null);
72
+ const id = Number(result.lastInsertRowid);
73
+ return {
74
+ id,
75
+ agentId,
76
+ scope,
77
+ domain: opts?.domain || null,
78
+ content,
79
+ confidence: opts?.confidence || 1.0,
80
+ visibility: resolvedVisibility,
81
+ sourceType: opts?.sourceType || 'conversation',
82
+ sourceSessionKey: opts?.sourceSessionKey || null,
83
+ sourceRef: opts?.sourceRef || null,
84
+ createdAt: now,
85
+ updatedAt: now,
86
+ expiresAt: opts?.expiresAt || null,
87
+ supersededBy: null,
88
+ decayScore: 0,
89
+ };
90
+ }
91
+ /**
92
+ * Get active facts for an agent.
93
+ */
94
+ getActiveFacts(agentId, opts) {
95
+ let sql = `
96
+ SELECT * FROM facts
97
+ WHERE agent_id = ?
98
+ AND superseded_by IS NULL
99
+ AND (expires_at IS NULL OR expires_at > datetime('now'))
100
+ AND decay_score < 0.8
101
+ `;
102
+ const params = [agentId];
103
+ if (opts?.scope) {
104
+ sql += ' AND scope = ?';
105
+ params.push(opts.scope);
106
+ }
107
+ if (opts?.domain) {
108
+ sql += ' AND domain = ?';
109
+ params.push(opts.domain);
110
+ }
111
+ if (opts?.minConfidence) {
112
+ sql += ' AND confidence >= ?';
113
+ params.push(opts.minConfidence);
114
+ }
115
+ sql += ' ORDER BY confidence DESC, decay_score ASC';
116
+ if (opts?.limit) {
117
+ sql += ' LIMIT ?';
118
+ params.push(opts.limit);
119
+ }
120
+ const rows = this.db.prepare(sql).all(...params);
121
+ return rows.map(parseFactRow);
122
+ }
123
+ /**
124
+ * Full-text search facts.
125
+ */
126
+ searchFacts(query, opts) {
127
+ const limit = opts?.limit || 20;
128
+ const hasFilters = !!(opts?.agentId || opts?.domain || opts?.visibility);
129
+ const innerLimit = hasFilters ? limit * 4 : limit;
130
+ // Two-phase: FTS in subquery, then filter on small set. See hybrid-retrieval.ts.
131
+ let sql = `
132
+ SELECT f.* FROM (
133
+ SELECT rowid, rank FROM facts_fts WHERE facts_fts MATCH ? ORDER BY rank LIMIT ?
134
+ ) sub
135
+ JOIN facts f ON f.id = sub.rowid
136
+ WHERE f.superseded_by IS NULL
137
+ AND f.decay_score < 0.8
138
+ `;
139
+ const params = [query, innerLimit];
140
+ if (opts?.agentId) {
141
+ sql += ' AND f.agent_id = ?';
142
+ params.push(opts.agentId);
143
+ }
144
+ if (opts?.domain) {
145
+ sql += ' AND f.domain = ?';
146
+ params.push(opts.domain);
147
+ }
148
+ if (opts?.visibility) {
149
+ sql += ' AND f.visibility = ?';
150
+ params.push(opts.visibility);
151
+ }
152
+ sql += ' ORDER BY sub.rank LIMIT ?';
153
+ params.push(limit);
154
+ const rows = this.db.prepare(sql).all(...params);
155
+ return rows.map(parseFactRow);
156
+ }
157
+ /**
158
+ * Mark an old fact as superseded by a new one.
159
+ *
160
+ * Sets `superseded_by` on the old fact row so it is excluded from active
161
+ * retrieval queries (both FTS and KNN paths check `superseded_by IS NULL`).
162
+ * Returns false if the fact is already superseded or does not exist.
163
+ */
164
+ markSuperseded(oldFactId, newFactId) {
165
+ const now = new Date().toISOString();
166
+ const result = this.db
167
+ .prepare(`
168
+ UPDATE facts
169
+ SET superseded_by = ?, updated_at = ?
170
+ WHERE id = ? AND superseded_by IS NULL
171
+ `)
172
+ .run(newFactId, now, oldFactId);
173
+ return result.changes > 0;
174
+ }
175
+ /**
176
+ * Find the most recent active fact for an agent whose content is a near-duplicate
177
+ * of the given content (same first 100 chars, different suffix, or same domain+topic).
178
+ * Used by the background indexer to detect supersedes relationships.
179
+ *
180
+ * Returns the existing fact id if a candidate is found, otherwise null.
181
+ */
182
+ findSupersedableByContent(agentId, content, opts) {
183
+ // Look for active facts from the same agent whose content starts with the
184
+ // same 60-character prefix (covers rephrased facts about the same topic).
185
+ const prefix = content.slice(0, 60);
186
+ const params = [agentId, `${prefix}%`];
187
+ let sql = `
188
+ SELECT id FROM facts
189
+ WHERE agent_id = ?
190
+ AND content LIKE ?
191
+ AND content != ?
192
+ AND superseded_by IS NULL
193
+ `;
194
+ params.push(content);
195
+ if (opts?.domain) {
196
+ sql += ' AND domain = ?';
197
+ params.push(opts.domain);
198
+ }
199
+ sql += ' ORDER BY created_at DESC LIMIT 1';
200
+ const row = this.db.prepare(sql).get(...params);
201
+ return row?.id ?? null;
202
+ }
203
+ /**
204
+ * Decay all facts by a fixed rate.
205
+ */
206
+ decayFacts(agentId, decayRate = 0.01) {
207
+ const result = this.db.prepare(`
208
+ UPDATE facts
209
+ SET decay_score = MIN(decay_score + ?, 1.0), updated_at = ?
210
+ WHERE agent_id = ? AND decay_score < 1.0
211
+ `).run(decayRate, nowIso(), agentId);
212
+ return result.changes;
213
+ }
214
+ /**
215
+ * Remove expired and fully decayed facts.
216
+ */
217
+ pruneFacts(agentId) {
218
+ const result = this.db.prepare(`
219
+ DELETE FROM facts
220
+ WHERE agent_id = ?
221
+ AND (
222
+ (expires_at IS NOT NULL AND expires_at < datetime('now'))
223
+ OR decay_score >= 1.0
224
+ )
225
+ `).run(agentId);
226
+ return result.changes;
227
+ }
228
+ /**
229
+ * Get fact count for an agent.
230
+ */
231
+ getFactCount(agentId) {
232
+ const row = this.db.prepare('SELECT COUNT(*) AS count FROM facts WHERE agent_id = ?').get(agentId);
233
+ return row.count;
234
+ }
235
+ }
236
+ //# sourceMappingURL=fact-store.js.map
@@ -0,0 +1,144 @@
1
+ /**
2
+ * hypermem Fleet Registry Store
3
+ *
4
+ * Agent roster, org structure, roles, capabilities.
5
+ * Lives in the central library DB.
6
+ * The operational map of the fleet.
7
+ */
8
+ import type { DatabaseSync } from 'node:sqlite';
9
+ export interface AgentCapability {
10
+ capType: 'skill' | 'tool' | 'mcp_server';
11
+ name: string;
12
+ version?: string;
13
+ source?: string;
14
+ config?: Record<string, unknown>;
15
+ status: string;
16
+ lastVerified?: string;
17
+ }
18
+ export interface FleetAgent {
19
+ id: string;
20
+ displayName: string;
21
+ tier: string;
22
+ orgId: string | null;
23
+ reportsTo: string | null;
24
+ domains: string[];
25
+ sessionKeys: string[];
26
+ status: string;
27
+ lastSeen: string | null;
28
+ createdAt: string;
29
+ updatedAt: string;
30
+ metadata: Record<string, unknown> | null;
31
+ capabilities: AgentCapability[] | null;
32
+ }
33
+ export interface FleetOrg {
34
+ id: string;
35
+ name: string;
36
+ leadAgentId: string | null;
37
+ mission: string | null;
38
+ createdAt: string;
39
+ }
40
+ export declare class FleetStore {
41
+ private readonly db;
42
+ constructor(db: DatabaseSync);
43
+ /**
44
+ * Register or update an agent.
45
+ */
46
+ upsertAgent(id: string, data: {
47
+ displayName?: string;
48
+ tier?: string;
49
+ orgId?: string;
50
+ reportsTo?: string;
51
+ domains?: string[];
52
+ sessionKeys?: string[];
53
+ status?: string;
54
+ metadata?: Record<string, unknown>;
55
+ }): FleetAgent;
56
+ /**
57
+ * Get an agent by ID.
58
+ */
59
+ getAgent(id: string): FleetAgent | null;
60
+ /**
61
+ * List all agents, optionally filtered.
62
+ */
63
+ listAgents(opts?: {
64
+ tier?: string;
65
+ orgId?: string;
66
+ status?: string;
67
+ }): FleetAgent[];
68
+ /**
69
+ * Update agent status and last_seen.
70
+ */
71
+ heartbeat(agentId: string, status?: string): void;
72
+ /**
73
+ * Find agents by domain.
74
+ */
75
+ findByDomain(domain: string): FleetAgent[];
76
+ /**
77
+ * Register or update an org.
78
+ */
79
+ upsertOrg(id: string, data: {
80
+ name: string;
81
+ leadAgentId?: string;
82
+ mission?: string;
83
+ }): FleetOrg;
84
+ /**
85
+ * Get an org by ID.
86
+ */
87
+ getOrg(id: string): FleetOrg | null;
88
+ /**
89
+ * List all orgs.
90
+ */
91
+ listOrgs(): FleetOrg[];
92
+ /**
93
+ * Get all agents in an org.
94
+ */
95
+ getOrgMembers(orgId: string): FleetAgent[];
96
+ /**
97
+ * Register or update a capability for an agent.
98
+ */
99
+ upsertCapability(agentId: string, cap: {
100
+ capType: AgentCapability['capType'];
101
+ name: string;
102
+ version?: string;
103
+ source?: string;
104
+ config?: Record<string, unknown>;
105
+ status?: string;
106
+ }): AgentCapability;
107
+ /**
108
+ * Bulk-sync capabilities for an agent (replace all of a given type).
109
+ */
110
+ syncCapabilities(agentId: string, capType: AgentCapability['capType'], caps: Array<{
111
+ name: string;
112
+ version?: string;
113
+ source?: string;
114
+ config?: Record<string, unknown>;
115
+ }>): void;
116
+ /**
117
+ * Get a specific capability.
118
+ */
119
+ getCapability(agentId: string, capType: string, name: string): AgentCapability | null;
120
+ /**
121
+ * List capabilities for an agent, optionally filtered by type.
122
+ */
123
+ getAgentCapabilities(agentId: string, capType?: string): AgentCapability[];
124
+ /**
125
+ * Find agents that have a specific capability.
126
+ */
127
+ findByCapability(capType: string, name: string): FleetAgent[];
128
+ /**
129
+ * Find agents that have ANY of the given capabilities.
130
+ */
131
+ findByAnyCapability(caps: Array<{
132
+ capType: string;
133
+ name: string;
134
+ }>): FleetAgent[];
135
+ /**
136
+ * Remove a capability from an agent.
137
+ */
138
+ removeCapability(agentId: string, capType: string, name: string): void;
139
+ /**
140
+ * Sync the denormalized capabilities JSON on fleet_agents.
141
+ */
142
+ private _syncCapabilitiesJson;
143
+ }
144
+ //# sourceMappingURL=fleet-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fleet-store.d.ts","sourceRoot":"","sources":["../src/fleet-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,276 @@
1
+ /**
2
+ * hypermem Fleet Registry Store
3
+ *
4
+ * Agent roster, org structure, roles, capabilities.
5
+ * Lives in the central library DB.
6
+ * The operational map of the fleet.
7
+ */
8
+ function nowIso() {
9
+ return new Date().toISOString();
10
+ }
11
+ function parseAgentRow(row) {
12
+ return {
13
+ id: row.id,
14
+ displayName: row.display_name,
15
+ tier: row.tier,
16
+ orgId: row.org_id || null,
17
+ reportsTo: row.reports_to || null,
18
+ domains: row.domains ? JSON.parse(row.domains) : [],
19
+ sessionKeys: row.session_keys ? JSON.parse(row.session_keys) : [],
20
+ status: row.status,
21
+ lastSeen: row.last_seen || null,
22
+ createdAt: row.created_at,
23
+ updatedAt: row.updated_at,
24
+ metadata: row.metadata ? JSON.parse(row.metadata) : null,
25
+ capabilities: row.capabilities ? JSON.parse(row.capabilities) : null,
26
+ };
27
+ }
28
+ function parseCapabilityRow(row) {
29
+ return {
30
+ capType: row.cap_type,
31
+ name: row.name,
32
+ version: row.version || undefined,
33
+ source: row.source || undefined,
34
+ config: row.config ? JSON.parse(row.config) : undefined,
35
+ status: row.status,
36
+ lastVerified: row.last_verified || undefined,
37
+ };
38
+ }
39
+ function parseOrgRow(row) {
40
+ return {
41
+ id: row.id,
42
+ name: row.name,
43
+ leadAgentId: row.lead_agent_id || null,
44
+ mission: row.mission || null,
45
+ createdAt: row.created_at,
46
+ };
47
+ }
48
+ export class FleetStore {
49
+ db;
50
+ constructor(db) {
51
+ this.db = db;
52
+ }
53
+ // ── Agents ──────────────────────────────────────────────────
54
+ /**
55
+ * Register or update an agent.
56
+ */
57
+ upsertAgent(id, data) {
58
+ const now = nowIso();
59
+ const existing = this.db.prepare('SELECT * FROM fleet_agents WHERE id = ?')
60
+ .get(id);
61
+ if (existing) {
62
+ this.db.prepare(`
63
+ UPDATE fleet_agents SET
64
+ display_name = COALESCE(?, display_name),
65
+ tier = COALESCE(?, tier),
66
+ org_id = COALESCE(?, org_id),
67
+ reports_to = COALESCE(?, reports_to),
68
+ domains = COALESCE(?, domains),
69
+ session_keys = COALESCE(?, session_keys),
70
+ status = COALESCE(?, status),
71
+ metadata = COALESCE(?, metadata),
72
+ last_seen = ?,
73
+ updated_at = ?
74
+ WHERE id = ?
75
+ `).run(data.displayName || null, data.tier || null, data.orgId || null, data.reportsTo || null, data.domains ? JSON.stringify(data.domains) : null, data.sessionKeys ? JSON.stringify(data.sessionKeys) : null, data.status || null, data.metadata ? JSON.stringify(data.metadata) : null, now, now, id);
76
+ }
77
+ else {
78
+ this.db.prepare(`
79
+ INSERT INTO fleet_agents (id, display_name, tier, org_id, reports_to,
80
+ domains, session_keys, status, last_seen, created_at, updated_at, metadata)
81
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
82
+ `).run(id, data.displayName || id, data.tier || 'unknown', data.orgId || null, data.reportsTo || null, data.domains ? JSON.stringify(data.domains) : '[]', data.sessionKeys ? JSON.stringify(data.sessionKeys) : '[]', data.status || 'active', now, now, now, data.metadata ? JSON.stringify(data.metadata) : null);
83
+ }
84
+ return this.getAgent(id);
85
+ }
86
+ /**
87
+ * Get an agent by ID.
88
+ */
89
+ getAgent(id) {
90
+ const row = this.db.prepare('SELECT * FROM fleet_agents WHERE id = ?')
91
+ .get(id);
92
+ return row ? parseAgentRow(row) : null;
93
+ }
94
+ /**
95
+ * List all agents, optionally filtered.
96
+ */
97
+ listAgents(opts) {
98
+ let sql = 'SELECT * FROM fleet_agents WHERE 1=1';
99
+ const params = [];
100
+ if (opts?.tier) {
101
+ sql += ' AND tier = ?';
102
+ params.push(opts.tier);
103
+ }
104
+ if (opts?.orgId) {
105
+ sql += ' AND org_id = ?';
106
+ params.push(opts.orgId);
107
+ }
108
+ if (opts?.status) {
109
+ sql += ' AND status = ?';
110
+ params.push(opts.status);
111
+ }
112
+ sql += ' ORDER BY tier, id';
113
+ const rows = this.db.prepare(sql).all(...params);
114
+ return rows.map(parseAgentRow);
115
+ }
116
+ /**
117
+ * Update agent status and last_seen.
118
+ */
119
+ heartbeat(agentId, status = 'active') {
120
+ const now = nowIso();
121
+ this.db.prepare('UPDATE fleet_agents SET status = ?, last_seen = ?, updated_at = ? WHERE id = ?').run(status, now, now, agentId);
122
+ }
123
+ /**
124
+ * Find agents by domain.
125
+ */
126
+ findByDomain(domain) {
127
+ const rows = this.db.prepare("SELECT * FROM fleet_agents WHERE domains LIKE ? AND status = 'active'").all(`%"${domain}"%`);
128
+ return rows.map(parseAgentRow);
129
+ }
130
+ // ── Orgs ────────────────────────────────────────────────────
131
+ /**
132
+ * Register or update an org.
133
+ */
134
+ upsertOrg(id, data) {
135
+ const now = nowIso();
136
+ this.db.prepare(`
137
+ INSERT INTO fleet_orgs (id, name, lead_agent_id, mission, created_at)
138
+ VALUES (?, ?, ?, ?, ?)
139
+ ON CONFLICT(id) DO UPDATE SET
140
+ name = excluded.name,
141
+ lead_agent_id = COALESCE(excluded.lead_agent_id, lead_agent_id),
142
+ mission = COALESCE(excluded.mission, mission)
143
+ `).run(id, data.name, data.leadAgentId || null, data.mission || null, now);
144
+ return this.getOrg(id);
145
+ }
146
+ /**
147
+ * Get an org by ID.
148
+ */
149
+ getOrg(id) {
150
+ const row = this.db.prepare('SELECT * FROM fleet_orgs WHERE id = ?')
151
+ .get(id);
152
+ return row ? parseOrgRow(row) : null;
153
+ }
154
+ /**
155
+ * List all orgs.
156
+ */
157
+ listOrgs() {
158
+ const rows = this.db.prepare('SELECT * FROM fleet_orgs ORDER BY name')
159
+ .all();
160
+ return rows.map(parseOrgRow);
161
+ }
162
+ /**
163
+ * Get all agents in an org.
164
+ */
165
+ getOrgMembers(orgId) {
166
+ const rows = this.db.prepare("SELECT * FROM fleet_agents WHERE org_id = ? ORDER BY tier, id").all(orgId);
167
+ return rows.map(parseAgentRow);
168
+ }
169
+ // ── Capabilities ────────────────────────────────────────────
170
+ /**
171
+ * Register or update a capability for an agent.
172
+ */
173
+ upsertCapability(agentId, cap) {
174
+ const now = nowIso();
175
+ this.db.prepare(`
176
+ INSERT INTO agent_capabilities (agent_id, cap_type, name, version, source, config, status, last_verified, created_at, updated_at)
177
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
178
+ ON CONFLICT(agent_id, cap_type, name) DO UPDATE SET
179
+ version = COALESCE(excluded.version, version),
180
+ source = COALESCE(excluded.source, source),
181
+ config = COALESCE(excluded.config, config),
182
+ status = excluded.status,
183
+ last_verified = excluded.last_verified,
184
+ updated_at = excluded.updated_at
185
+ `).run(agentId, cap.capType, cap.name, cap.version || null, cap.source || null, cap.config ? JSON.stringify(cap.config) : null, cap.status || 'active', now, now, now);
186
+ // Also update the denormalized JSON on fleet_agents
187
+ this._syncCapabilitiesJson(agentId);
188
+ return this.getCapability(agentId, cap.capType, cap.name);
189
+ }
190
+ /**
191
+ * Bulk-sync capabilities for an agent (replace all of a given type).
192
+ */
193
+ syncCapabilities(agentId, capType, caps) {
194
+ const now = nowIso();
195
+ const capNames = caps.map(c => c.name);
196
+ // Mark missing ones as removed
197
+ const existing = this.db.prepare('SELECT name FROM agent_capabilities WHERE agent_id = ? AND cap_type = ? AND status = ?').all(agentId, capType, 'active');
198
+ for (const row of existing) {
199
+ if (!capNames.includes(row.name)) {
200
+ this.db.prepare('UPDATE agent_capabilities SET status = ?, updated_at = ? WHERE agent_id = ? AND cap_type = ? AND name = ?').run('removed', now, agentId, capType, row.name);
201
+ }
202
+ }
203
+ // Upsert current ones
204
+ for (const cap of caps) {
205
+ this.upsertCapability(agentId, { capType, ...cap });
206
+ }
207
+ this._syncCapabilitiesJson(agentId);
208
+ }
209
+ /**
210
+ * Get a specific capability.
211
+ */
212
+ getCapability(agentId, capType, name) {
213
+ const row = this.db.prepare('SELECT * FROM agent_capabilities WHERE agent_id = ? AND cap_type = ? AND name = ?').get(agentId, capType, name);
214
+ return row ? parseCapabilityRow(row) : null;
215
+ }
216
+ /**
217
+ * List capabilities for an agent, optionally filtered by type.
218
+ */
219
+ getAgentCapabilities(agentId, capType) {
220
+ let sql = 'SELECT * FROM agent_capabilities WHERE agent_id = ? AND status = ?';
221
+ const params = [agentId, 'active'];
222
+ if (capType) {
223
+ sql += ' AND cap_type = ?';
224
+ params.push(capType);
225
+ }
226
+ sql += ' ORDER BY cap_type, name';
227
+ const rows = this.db.prepare(sql).all(...params);
228
+ return rows.map(parseCapabilityRow);
229
+ }
230
+ /**
231
+ * Find agents that have a specific capability.
232
+ */
233
+ findByCapability(capType, name) {
234
+ const rows = this.db.prepare(`
235
+ SELECT fa.* FROM fleet_agents fa
236
+ INNER JOIN agent_capabilities ac ON ac.agent_id = fa.id
237
+ WHERE ac.cap_type = ? AND ac.name = ? AND ac.status = 'active' AND fa.status = 'active'
238
+ ORDER BY fa.tier, fa.id
239
+ `).all(capType, name);
240
+ return rows.map(parseAgentRow);
241
+ }
242
+ /**
243
+ * Find agents that have ANY of the given capabilities.
244
+ */
245
+ findByAnyCapability(caps) {
246
+ if (caps.length === 0)
247
+ return [];
248
+ const conditions = caps.map(() => '(ac.cap_type = ? AND ac.name = ?)').join(' OR ');
249
+ const params = caps.flatMap(c => [c.capType, c.name]);
250
+ const rows = this.db.prepare(`
251
+ SELECT DISTINCT fa.* FROM fleet_agents fa
252
+ INNER JOIN agent_capabilities ac ON ac.agent_id = fa.id
253
+ WHERE (${conditions}) AND ac.status = 'active' AND fa.status = 'active'
254
+ ORDER BY fa.tier, fa.id
255
+ `).all(...params);
256
+ return rows.map(parseAgentRow);
257
+ }
258
+ /**
259
+ * Remove a capability from an agent.
260
+ */
261
+ removeCapability(agentId, capType, name) {
262
+ const now = nowIso();
263
+ this.db.prepare('UPDATE agent_capabilities SET status = ?, updated_at = ? WHERE agent_id = ? AND cap_type = ? AND name = ?').run('removed', now, agentId, capType, name);
264
+ this._syncCapabilitiesJson(agentId);
265
+ }
266
+ // ── Internal ────────────────────────────────────────────────
267
+ /**
268
+ * Sync the denormalized capabilities JSON on fleet_agents.
269
+ */
270
+ _syncCapabilitiesJson(agentId) {
271
+ const caps = this.getAgentCapabilities(agentId);
272
+ const now = nowIso();
273
+ this.db.prepare('UPDATE fleet_agents SET capabilities = ?, updated_at = ? WHERE id = ?').run(JSON.stringify(caps), now, agentId);
274
+ }
275
+ }
276
+ //# sourceMappingURL=fleet-store.js.map