mark-improving-agent 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +335 -0
  2. package/VERSION +1 -0
  3. package/bin/cli.js +12 -0
  4. package/dist/agent/context.js +78 -0
  5. package/dist/agent/index.js +6 -0
  6. package/dist/agent/runtime.js +195 -0
  7. package/dist/agent/task-graph.js +209 -0
  8. package/dist/agent/types.js +1 -0
  9. package/dist/cli/index.js +206 -0
  10. package/dist/core/cognition/active-inference.js +296 -0
  11. package/dist/core/cognition/cognitive-architecture.js +263 -0
  12. package/dist/core/cognition/dual-process.js +102 -0
  13. package/dist/core/cognition/index.js +13 -0
  14. package/dist/core/cognition/learning-from-failure.js +184 -0
  15. package/dist/core/cognition/meta-agent.js +407 -0
  16. package/dist/core/cognition/metacognition.js +322 -0
  17. package/dist/core/cognition/react.js +177 -0
  18. package/dist/core/cognition/retrieval-anchor.js +99 -0
  19. package/dist/core/cognition/self-evolution.js +294 -0
  20. package/dist/core/cognition/self-verification.js +190 -0
  21. package/dist/core/cognition/thought-graph.js +495 -0
  22. package/dist/core/cognition/tool-augmented-llm.js +188 -0
  23. package/dist/core/cognition/tool-execution-verifier.js +204 -0
  24. package/dist/core/collaboration/agentic-loop.js +165 -0
  25. package/dist/core/collaboration/index.js +3 -0
  26. package/dist/core/collaboration/multi-agent-system.js +186 -0
  27. package/dist/core/collaboration/multi-agent.js +110 -0
  28. package/dist/core/consciousness/emotion-engine.js +101 -0
  29. package/dist/core/consciousness/flow-machine.js +121 -0
  30. package/dist/core/consciousness/index.js +4 -0
  31. package/dist/core/consciousness/personality.js +103 -0
  32. package/dist/core/consciousness/types.js +1 -0
  33. package/dist/core/emotional-protocol.js +54 -0
  34. package/dist/core/evolution/engine.js +194 -0
  35. package/dist/core/evolution/goal-engine.js +153 -0
  36. package/dist/core/evolution/index.js +6 -0
  37. package/dist/core/evolution/meta-learning.js +172 -0
  38. package/dist/core/evolution/reflection.js +158 -0
  39. package/dist/core/evolution/self-healer.js +139 -0
  40. package/dist/core/evolution/types.js +1 -0
  41. package/dist/core/healing-rl.js +266 -0
  42. package/dist/core/heartbeat.js +408 -0
  43. package/dist/core/identity/index.js +3 -0
  44. package/dist/core/identity/reflexion.js +165 -0
  45. package/dist/core/identity/self-model.js +274 -0
  46. package/dist/core/identity/self-verifier.js +158 -0
  47. package/dist/core/identity/types.js +12 -0
  48. package/dist/core/lesson-bank.js +301 -0
  49. package/dist/core/memory/adaptive-rag.js +440 -0
  50. package/dist/core/memory/archive-store.js +187 -0
  51. package/dist/core/memory/dream-consolidation.js +366 -0
  52. package/dist/core/memory/embedder.js +130 -0
  53. package/dist/core/memory/hopfield-network.js +128 -0
  54. package/dist/core/memory/index.js +9 -0
  55. package/dist/core/memory/knowledge-graph.js +151 -0
  56. package/dist/core/memory/spaced-repetition.js +113 -0
  57. package/dist/core/memory/store.js +404 -0
  58. package/dist/core/memory/types.js +1 -0
  59. package/dist/core/psychology/analysis.js +456 -0
  60. package/dist/core/psychology/index.js +1 -0
  61. package/dist/core/rollback-manager.js +191 -0
  62. package/dist/core/security/index.js +1 -0
  63. package/dist/core/security/privacy.js +132 -0
  64. package/dist/core/truth-teller.js +253 -0
  65. package/dist/core/truthfulness.js +99 -0
  66. package/dist/core/types.js +2 -0
  67. package/dist/event/bus.js +47 -0
  68. package/dist/index.js +8 -0
  69. package/dist/skills/dag.js +181 -0
  70. package/dist/skills/index.js +5 -0
  71. package/dist/skills/registry.js +40 -0
  72. package/dist/skills/types.js +1 -0
  73. package/dist/storage/archive.js +77 -0
  74. package/dist/storage/checkpoint.js +119 -0
  75. package/dist/storage/types.js +1 -0
  76. package/dist/utils/config.js +81 -0
  77. package/dist/utils/logger.js +49 -0
  78. package/dist/version.js +1 -0
  79. package/package.json +37 -0
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Knowledge Graph - Agent K Architecture Integration
3
+ *
4
+ * Structured memory with temporal context and relationship tracking.
5
+ * Based on Agent K paper: 4-layer cognitive architecture with KG.
6
+ */
7
+ import { randomUUID } from 'crypto';
8
+ import { atomicWriteJSON, ensureDir, readJSON } from '../../storage/archive.js';
9
+ const MAX_CONNECTIONS_PER_NODE = 20;
10
+ function createDefaultNode(partial) {
11
+ const now = Date.now();
12
+ return {
13
+ ...partial,
14
+ id: `kg-${now}-${randomUUID().slice(0, 8)}`,
15
+ createdAt: now,
16
+ lastAccessed: now,
17
+ };
18
+ }
19
+ export function createKnowledgeGraph(dataDir) {
20
+ let nodes = new Map();
21
+ let edges = new Map();
22
+ let isDirty = false;
23
+ const nodesFile = `${dataDir}/knowledge-graph-nodes.json`;
24
+ const edgesFile = `${dataDir}/knowledge-graph-edges.json`;
25
+ async function persist() {
26
+ if (!isDirty)
27
+ return;
28
+ await atomicWriteJSON(nodesFile, Object.fromEntries(nodes));
29
+ await atomicWriteJSON(edgesFile, Object.fromEntries(edges));
30
+ isDirty = false;
31
+ }
32
+ async function boot() {
33
+ await ensureDir(dataDir);
34
+ const loadedNodes = await readJSON(nodesFile, {});
35
+ const loadedEdges = await readJSON(edgesFile, {});
36
+ nodes = new Map(Object.entries(loadedNodes || {}));
37
+ edges = new Map(Object.entries(loadedEdges || {}));
38
+ }
39
+ function addNode(partial) {
40
+ const node = createDefaultNode(partial);
41
+ nodes.set(node.id, node);
42
+ isDirty = true;
43
+ return node;
44
+ }
45
+ function addEdge(partial) {
46
+ // Validate both nodes exist
47
+ if (!nodes.has(partial.sourceId) || !nodes.has(partial.targetId)) {
48
+ return null;
49
+ }
50
+ // Limit connections per node
51
+ const sourceNode = nodes.get(partial.sourceId);
52
+ if (sourceNode.connections.length >= MAX_CONNECTIONS_PER_NODE) {
53
+ return null;
54
+ }
55
+ const edge = {
56
+ ...partial,
57
+ id: `kge-${Date.now()}-${randomUUID().slice(0, 8)}`,
58
+ createdAt: Date.now(),
59
+ };
60
+ edges.set(edge.id, edge);
61
+ // Update node connections
62
+ if (!sourceNode.connections.includes(partial.targetId)) {
63
+ sourceNode.connections.push(partial.targetId);
64
+ }
65
+ const targetNode = nodes.get(partial.targetId);
66
+ if (partial.bidirectional && !targetNode.connections.includes(partial.sourceId)) {
67
+ targetNode.connections.push(partial.sourceId);
68
+ }
69
+ isDirty = true;
70
+ return edge;
71
+ }
72
+ function getNode(id) {
73
+ const node = nodes.get(id) ?? null;
74
+ if (node) {
75
+ node.lastAccessed = Date.now();
76
+ isDirty = true;
77
+ }
78
+ return node;
79
+ }
80
+ function getConnectedNodes(nodeId, relation) {
81
+ const node = nodes.get(nodeId);
82
+ if (!node)
83
+ return [];
84
+ const connected = [];
85
+ for (const edge of edges.values()) {
86
+ if (edge.sourceId === nodeId || (edge.bidirectional && edge.targetId === nodeId)) {
87
+ if (relation && edge.relation !== relation)
88
+ continue;
89
+ const connectedId = edge.sourceId === nodeId ? edge.targetId : edge.sourceId;
90
+ const connectedNode = nodes.get(connectedId);
91
+ if (connectedNode) {
92
+ connectedNode.lastAccessed = Date.now();
93
+ connected.push(connectedNode);
94
+ }
95
+ }
96
+ }
97
+ return connected;
98
+ }
99
+ function search(query) {
100
+ const lowerQuery = query.toLowerCase();
101
+ const results = [];
102
+ for (const node of nodes.values()) {
103
+ if (node.name.toLowerCase().includes(lowerQuery) ||
104
+ node.description.toLowerCase().includes(lowerQuery)) {
105
+ results.push(node);
106
+ }
107
+ }
108
+ return results.sort((a, b) => b.importance - a.importance);
109
+ }
110
+ /**
111
+ * Reflect on a node - increment reflection count and update status
112
+ * Based on Agent K's reflection mechanism
113
+ */
114
+ function reflect(nodeId) {
115
+ const node = nodes.get(nodeId);
116
+ if (!node)
117
+ return null;
118
+ node.reflectionCount = (node.reflectionCount ?? 0) + 1;
119
+ node.lastAccessed = Date.now();
120
+ isDirty = true;
121
+ return node;
122
+ }
123
+ function getStats() {
124
+ let totalConnections = 0;
125
+ let mostConnected = [];
126
+ for (const node of nodes.values()) {
127
+ totalConnections += node.connections.length;
128
+ if (node.connections.length > 0) {
129
+ mostConnected.push({ id: node.id, count: node.connections.length });
130
+ }
131
+ }
132
+ mostConnected.sort((a, b) => b.count - a.count);
133
+ return {
134
+ nodes: nodes.size,
135
+ edges: edges.size,
136
+ avgConnections: nodes.size > 0 ? totalConnections / nodes.size : 0,
137
+ mostConnected: mostConnected.slice(0, 5).map(m => m.id),
138
+ };
139
+ }
140
+ return {
141
+ addNode,
142
+ addEdge,
143
+ getNode,
144
+ getConnectedNodes,
145
+ search,
146
+ reflect,
147
+ getStats,
148
+ persist,
149
+ boot,
150
+ };
151
+ }
@@ -0,0 +1,113 @@
1
+ // SM-2 Spaced Repetition Algorithm
2
+ // Based on SuperMemo 2 algorithm for memory reinforcement
3
+ import { randomUUID } from 'crypto';
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
5
+ import { dirname } from 'path';
6
+ export function createSpacedRepetition(dataDir) {
7
+ const stateFile = `${dataDir}/sr-state.json`;
8
+ let state = { entries: {} };
9
+ function loadState() {
10
+ try {
11
+ if (existsSync(stateFile)) {
12
+ const raw = readFileSync(stateFile, 'utf-8');
13
+ const parsed = JSON.parse(raw);
14
+ state = parsed;
15
+ }
16
+ }
17
+ catch {
18
+ // Use defaults
19
+ }
20
+ }
21
+ function persistState() {
22
+ try {
23
+ const dir = dirname(stateFile);
24
+ if (!existsSync(dir)) {
25
+ mkdirSync(dir, { recursive: true });
26
+ }
27
+ const tmp = stateFile + '.tmp.' + randomUUID().slice(0, 8);
28
+ writeFileSync(tmp, JSON.stringify(state, null, 2), 'utf-8');
29
+ require('fs').renameSync(tmp, stateFile);
30
+ }
31
+ catch {
32
+ // Persistence failure is non-fatal
33
+ }
34
+ }
35
+ // Load state on creation
36
+ loadState();
37
+ function recordReview(id, quality) {
38
+ const now = Date.now();
39
+ let entry = state.entries[id];
40
+ if (!entry) {
41
+ entry = {
42
+ id,
43
+ easeFactor: 2.5,
44
+ interval: 0,
45
+ repetitions: 0,
46
+ nextReviewAt: now,
47
+ lastReviewedAt: now,
48
+ };
49
+ }
50
+ // SM-2 algorithm
51
+ if (quality >= 3) {
52
+ // Successful recall
53
+ if (entry.repetitions === 0) {
54
+ entry.interval = 1;
55
+ }
56
+ else if (entry.repetitions === 1) {
57
+ entry.interval = 6;
58
+ }
59
+ else {
60
+ entry.interval = Math.round(entry.interval * entry.easeFactor);
61
+ }
62
+ entry.repetitions++;
63
+ }
64
+ else {
65
+ // Failed recall - reset
66
+ entry.interval = 1;
67
+ entry.repetitions = 0;
68
+ }
69
+ // Update ease factor: EF' = EF + (0.1 - (5-q) * (0.08 + (5-q) * 0.02))
70
+ const q = quality;
71
+ entry.easeFactor = Math.max(1.3, entry.easeFactor + (0.1 - (5 - q) * (0.08 + (5 - q) * 0.02)));
72
+ // Set next review time
73
+ entry.nextReviewAt = now + entry.interval * 24 * 60 * 60 * 1000;
74
+ entry.lastReviewedAt = now;
75
+ state.entries[id] = entry;
76
+ persistState();
77
+ }
78
+ function getNextReviewDate(id) {
79
+ const entry = state.entries[id];
80
+ return entry ? entry.nextReviewAt : null;
81
+ }
82
+ function getDueEntries(entries) {
83
+ const now = Date.now();
84
+ return entries.filter(entry => {
85
+ const srEntry = state.entries[entry.id];
86
+ return !srEntry || srEntry.nextReviewAt <= now;
87
+ });
88
+ }
89
+ function getSpacedRepetitionStats() {
90
+ const now = Date.now();
91
+ let dueCount = 0;
92
+ let masteredCount = 0;
93
+ let learningCount = 0;
94
+ for (const entry of Object.values(state.entries)) {
95
+ if (entry.nextReviewAt <= now) {
96
+ dueCount++;
97
+ }
98
+ if (entry.repetitions >= 5 && entry.easeFactor >= 2.5) {
99
+ masteredCount++;
100
+ }
101
+ else if (entry.repetitions > 0) {
102
+ learningCount++;
103
+ }
104
+ }
105
+ return { dueCount, masteredCount, learningCount };
106
+ }
107
+ return {
108
+ recordReview,
109
+ getNextReviewDate,
110
+ getDueEntries,
111
+ getSpacedRepetitionStats,
112
+ };
113
+ }
@@ -0,0 +1,404 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { atomicWriteJSON, ensureDir, readJSON } from '../../storage/archive.js';
3
+ import { cosineSimilarity } from './embedder.js';
4
+ import { events } from '../../event/bus.js';
5
+ import { createDreamEngine } from './dream-consolidation.js';
6
+ import { createSpacedRepetition } from './spaced-repetition.js';
7
+ const STORE_VERSION = 1;
8
+ const SAVE_DEBOUNCE_MS = 500;
9
+ /**
10
+ * Source weight constants for importance scoring.
11
+ */
12
+ const SOURCE_WEIGHTS = {
13
+ user_correction: 1.0,
14
+ decision_verified: 0.85,
15
+ self: 0.7,
16
+ system: 0.5,
17
+ };
18
+ function computeImportance(baseImportance, source, timestamp) {
19
+ const sourceWeight = source ? SOURCE_WEIGHTS[source] ?? 0.5 : 0.5;
20
+ const ageMs = Date.now() - timestamp;
21
+ const ageDays = ageMs / (1000 * 60 * 60 * 24);
22
+ const recencyBonus = Math.max(0, 0.1 - ageDays * 0.01);
23
+ return Math.min(1.0, baseImportance * sourceWeight + recencyBonus);
24
+ }
25
+ export function createMemoryStore(options) {
26
+ const dataDir = options.dataDir;
27
+ const embedder = options.embedder;
28
+ const learnedTtlDays = options.learnedTtlDays ?? 30;
29
+ const maxEphemeral = options.maxEphemeral ?? 1000;
30
+ // Three separate in-memory Maps
31
+ const coreMap = new Map();
32
+ const learnedMap = new Map();
33
+ const ephemeralMap = new Map();
34
+ // Dream engine and spaced repetition
35
+ const dreamEngine = createDreamEngine(dataDir);
36
+ const spacedRepetition = createSpacedRepetition(dataDir);
37
+ // File paths
38
+ const coreFile = `${dataDir}/core.json`;
39
+ const learnedFile = `${dataDir}/learned.json`;
40
+ const ephemeralFile = `${dataDir}/ephemeral.json`;
41
+ // Dirty flags and debounce timers
42
+ let coreDirty = false;
43
+ let learnedDirty = false;
44
+ let ephemeralDirty = false;
45
+ let saveTimer = null;
46
+ function getTierMap(tier) {
47
+ switch (tier) {
48
+ case 'core': return coreMap;
49
+ case 'learned': return learnedMap;
50
+ case 'ephemeral': return ephemeralMap;
51
+ }
52
+ }
53
+ function getTierFile(tier) {
54
+ switch (tier) {
55
+ case 'core': return coreFile;
56
+ case 'learned': return learnedFile;
57
+ case 'ephemeral': return ephemeralFile;
58
+ }
59
+ }
60
+ function markDirty(tier) {
61
+ switch (tier) {
62
+ case 'core':
63
+ coreDirty = true;
64
+ break;
65
+ case 'learned':
66
+ learnedDirty = true;
67
+ break;
68
+ case 'ephemeral':
69
+ ephemeralDirty = true;
70
+ break;
71
+ }
72
+ scheduleSave();
73
+ }
74
+ async function saveTier(tier) {
75
+ const map = getTierMap(tier);
76
+ const file = getTierFile(tier);
77
+ const data = { entries: Object.fromEntries(map) };
78
+ await atomicWriteJSON(file, data);
79
+ switch (tier) {
80
+ case 'core':
81
+ coreDirty = false;
82
+ break;
83
+ case 'learned':
84
+ learnedDirty = false;
85
+ break;
86
+ case 'ephemeral':
87
+ ephemeralDirty = false;
88
+ break;
89
+ }
90
+ }
91
+ function scheduleSave() {
92
+ if (saveTimer !== null) {
93
+ clearTimeout(saveTimer);
94
+ }
95
+ saveTimer = setTimeout(async () => {
96
+ try {
97
+ await Promise.all([
98
+ coreDirty ? saveTier('core') : Promise.resolve(),
99
+ learnedDirty ? saveTier('learned') : Promise.resolve(),
100
+ ephemeralDirty ? saveTier('ephemeral') : Promise.resolve(),
101
+ ]);
102
+ }
103
+ catch (err) {
104
+ console.error('Memory store auto-save failed:', err);
105
+ }
106
+ saveTimer = null;
107
+ }, SAVE_DEBOUNCE_MS);
108
+ }
109
+ async function saveAllIfDirty() {
110
+ await Promise.all([
111
+ coreDirty ? saveTier('core') : Promise.resolve(),
112
+ learnedDirty ? saveTier('learned') : Promise.resolve(),
113
+ ephemeralDirty ? saveTier('ephemeral') : Promise.resolve(),
114
+ ]);
115
+ }
116
+ async function evictOldestEphemeral() {
117
+ const entries = Array.from(ephemeralMap.values());
118
+ entries.sort((a, b) => a.timestamp - b.timestamp);
119
+ const toRemove = Math.ceil(ephemeralMap.size * 0.1); // Remove 10%
120
+ for (let i = 0; i < toRemove && i < entries.length; i++) {
121
+ ephemeralMap.delete(entries[i].id);
122
+ }
123
+ if (toRemove > 0) {
124
+ markDirty('ephemeral');
125
+ }
126
+ }
127
+ // Memory store implementation with Agent K enhancements
128
+ const store = (content, importance, tags = [], tier = 'learned', source = 'system', options) => {
129
+ const id = randomUUID();
130
+ const timestamp = Date.now();
131
+ const embedding = embedder.embed(content);
132
+ // Determine effective tier based on importance
133
+ let effectiveTier = tier;
134
+ if (tier === 'learned' && importance > 0.85) {
135
+ effectiveTier = 'core';
136
+ }
137
+ const entry = {
138
+ id,
139
+ tier: effectiveTier,
140
+ content,
141
+ importance,
142
+ tags,
143
+ timestamp,
144
+ accessCount: 0,
145
+ lastAccessed: timestamp,
146
+ ttl: effectiveTier === 'learned' ? learnedTtlDays * 24 * 60 * 60 * 1000 : undefined,
147
+ source,
148
+ embedding,
149
+ // Agent K enhancements
150
+ temporalContext: options?.temporalContext ?? {
151
+ isOngoing: true,
152
+ },
153
+ reflectionCount: 0,
154
+ verificationStatus: 'unverified',
155
+ };
156
+ getTierMap(effectiveTier).set(id, entry);
157
+ markDirty(effectiveTier);
158
+ // Record successful store as spaced repetition review (quality=4)
159
+ spacedRepetition.recordReview(id, 4);
160
+ // Enforce ephemeral limit
161
+ if (ephemeralMap.size > maxEphemeral) {
162
+ evictOldestEphemeral();
163
+ }
164
+ events.emit('memory:store', entry);
165
+ return entry;
166
+ };
167
+ const recall = async (id) => {
168
+ let entry = coreMap.get(id) ?? learnedMap.get(id) ?? ephemeralMap.get(id);
169
+ if (entry) {
170
+ entry = { ...entry, accessCount: entry.accessCount + 1, lastAccessed: Date.now() };
171
+ getTierMap(entry.tier).set(id, entry);
172
+ events.emit('memory:recall', { id: entry.id, found: true });
173
+ }
174
+ return entry ?? null;
175
+ };
176
+ const forget = async (id) => {
177
+ for (const [tid, map] of [['core', coreMap], ['learned', learnedMap], ['ephemeral', ephemeralMap]]) {
178
+ if (map.has(id)) {
179
+ map.delete(id);
180
+ markDirty(tid);
181
+ return true;
182
+ }
183
+ }
184
+ return false;
185
+ };
186
+ const search = (query, limit = 10) => {
187
+ const queryEmbedding = embedder.embed(query);
188
+ const results = [];
189
+ const allEntries = [
190
+ ...Array.from(coreMap.values()),
191
+ ...Array.from(learnedMap.values()),
192
+ ...Array.from(ephemeralMap.values()),
193
+ ];
194
+ for (const entry of allEntries) {
195
+ if (!entry.embedding)
196
+ continue;
197
+ const score = cosineSimilarity(queryEmbedding, entry.embedding);
198
+ if (score > 0.5) {
199
+ results.push({
200
+ entry,
201
+ score,
202
+ reason: 'semantic',
203
+ });
204
+ }
205
+ }
206
+ // Sort by score descending
207
+ results.sort((a, b) => b.score - a.score);
208
+ // Reinforce memories on successful recall (spaced repetition)
209
+ for (const result of results) {
210
+ spacedRepetition.recordReview(result.entry.id, 4);
211
+ }
212
+ return results.slice(0, limit);
213
+ };
214
+ const searchByTags = async (tags, limit = 20) => {
215
+ const lowerTags = tags.map(t => t.toLowerCase());
216
+ const results = [];
217
+ const allEntries = [
218
+ ...Array.from(coreMap.values()),
219
+ ...Array.from(learnedMap.values()),
220
+ ...Array.from(ephemeralMap.values()),
221
+ ];
222
+ for (const entry of allEntries) {
223
+ if (entry.tags.some(tag => lowerTags.includes(tag.toLowerCase()))) {
224
+ results.push(entry);
225
+ }
226
+ }
227
+ // Sort by importance descending
228
+ results.sort((a, b) => b.importance - a.importance);
229
+ return results.slice(0, limit);
230
+ };
231
+ const recallRecent = async (limit = 20) => {
232
+ const results = [
233
+ ...Array.from(coreMap.values()),
234
+ ...Array.from(learnedMap.values()),
235
+ ...Array.from(ephemeralMap.values()),
236
+ ];
237
+ results.sort((a, b) => b.timestamp - a.timestamp);
238
+ return results.slice(0, limit);
239
+ };
240
+ const recallHighValue = async (minImportance = 0.8) => {
241
+ const results = [
242
+ ...Array.from(coreMap.values()),
243
+ ...Array.from(learnedMap.values()),
244
+ ...Array.from(ephemeralMap.values()),
245
+ ];
246
+ return results
247
+ .filter(e => e.importance >= minImportance)
248
+ .sort((a, b) => b.importance - a.importance);
249
+ };
250
+ const distill = async () => {
251
+ let promoted = 0;
252
+ const now = Date.now();
253
+ for (const [id, entry] of learnedMap) {
254
+ // Promote learned entries with high importance and age
255
+ const ageMs = now - entry.timestamp;
256
+ const ageDays = ageMs / (1000 * 60 * 60 * 24);
257
+ if (entry.importance > 0.85 || ageDays > 60) {
258
+ learnedMap.delete(id);
259
+ const promotedEntry = {
260
+ ...entry,
261
+ tier: 'core',
262
+ importance: Math.min(1, entry.importance + 0.05),
263
+ };
264
+ coreMap.set(id, promotedEntry);
265
+ markDirty('learned');
266
+ markDirty('core');
267
+ promoted++;
268
+ }
269
+ }
270
+ return promoted;
271
+ };
272
+ const prune = async () => {
273
+ let pruned = 0;
274
+ const now = Date.now();
275
+ for (const [id, entry] of learnedMap) {
276
+ if (entry.ttl) {
277
+ const ageMs = now - entry.timestamp;
278
+ if (ageMs > entry.ttl) {
279
+ learnedMap.delete(id);
280
+ markDirty('learned');
281
+ pruned++;
282
+ }
283
+ }
284
+ }
285
+ return pruned;
286
+ };
287
+ const getStats = async () => {
288
+ const allEntries = [
289
+ ...Array.from(coreMap.values()),
290
+ ...Array.from(learnedMap.values()),
291
+ ...Array.from(ephemeralMap.values()),
292
+ ];
293
+ const avgImportance = allEntries.length > 0
294
+ ? allEntries.reduce((sum, e) => sum + e.importance, 0) / allEntries.length
295
+ : 0;
296
+ return {
297
+ core: coreMap.size,
298
+ learned: learnedMap.size,
299
+ ephemeral: ephemeralMap.size,
300
+ avgImportance,
301
+ archived: 0,
302
+ };
303
+ };
304
+ const boot = async () => {
305
+ await ensureDir(dataDir);
306
+ const loadTier = async (file) => {
307
+ const map = new Map();
308
+ const data = await readJSON(file, { entries: {} });
309
+ if (data && data.entries) {
310
+ for (const [id, entry] of Object.entries(data.entries)) {
311
+ map.set(id, entry);
312
+ }
313
+ }
314
+ return map;
315
+ };
316
+ try {
317
+ const loadedCore = await loadTier(coreFile);
318
+ if (loadedCore.size > 0)
319
+ coreMap.clear();
320
+ loadedCore.forEach((v, k) => coreMap.set(k, v));
321
+ }
322
+ catch (err) {
323
+ console.error(`Failed to load core tier from ${coreFile}:`, err);
324
+ }
325
+ try {
326
+ const loadedLearned = await loadTier(learnedFile);
327
+ if (loadedLearned.size > 0)
328
+ learnedMap.clear();
329
+ loadedLearned.forEach((v, k) => learnedMap.set(k, v));
330
+ }
331
+ catch (err) {
332
+ console.error(`Failed to load learned tier from ${learnedFile}:`, err);
333
+ }
334
+ try {
335
+ const loadedEphemeral = await loadTier(ephemeralFile);
336
+ if (loadedEphemeral.size > 0)
337
+ ephemeralMap.clear();
338
+ loadedEphemeral.forEach((v, k) => ephemeralMap.set(k, v));
339
+ }
340
+ catch (err) {
341
+ console.error(`Failed to load ephemeral tier from ${ephemeralFile}:`, err);
342
+ }
343
+ };
344
+ const shutdown = async () => {
345
+ if (saveTimer) {
346
+ clearTimeout(saveTimer);
347
+ saveTimer = null;
348
+ }
349
+ await saveAllIfDirty();
350
+ };
351
+ const exportAll = () => {
352
+ const data = {
353
+ version: STORE_VERSION,
354
+ core: { entries: Object.fromEntries(coreMap) },
355
+ learned: { entries: Object.fromEntries(learnedMap) },
356
+ ephemeral: { entries: Object.fromEntries(ephemeralMap) },
357
+ };
358
+ return JSON.stringify(data, null, 2);
359
+ };
360
+ // Dream consolidation
361
+ async function dream() {
362
+ const allEntries = [
363
+ ...Array.from(coreMap.values()),
364
+ ...Array.from(learnedMap.values()),
365
+ ...Array.from(ephemeralMap.values()),
366
+ ];
367
+ return dreamEngine.dream(allEntries);
368
+ }
369
+ // Spaced repetition
370
+ function getDueMemories() {
371
+ const allEntries = [
372
+ ...Array.from(coreMap.values()),
373
+ ...Array.from(learnedMap.values()),
374
+ ...Array.from(ephemeralMap.values()),
375
+ ];
376
+ return spacedRepetition.getDueEntries(allEntries);
377
+ }
378
+ function recordReview(id, quality) {
379
+ spacedRepetition.recordReview(id, quality);
380
+ }
381
+ // Memory health
382
+ function getMemoryHealth() {
383
+ return dreamEngine.getMemoryHealth();
384
+ }
385
+ return {
386
+ store,
387
+ recall,
388
+ forget,
389
+ search,
390
+ searchByTags,
391
+ recallRecent,
392
+ recallHighValue,
393
+ distill,
394
+ prune,
395
+ getStats,
396
+ boot,
397
+ shutdown,
398
+ exportAll,
399
+ dream,
400
+ getDueMemories,
401
+ recordReview,
402
+ getMemoryHealth,
403
+ };
404
+ }
@@ -0,0 +1 @@
1
+ export {};