Smart Contract

CognitiveMemory

A.91d0a5b7c9832a8b.CognitiveMemory

Valid From

143,525,935

Deployed

1d ago
Feb 27, 2026, 12:09:30 AM UTC

Dependents

0 imports
1// CognitiveMemory.cdc
2// Cognitive memory layer for FlowClaw AI agents on Flow.
3// Implements the four biological memory types (episodic, semantic, procedural, self-model),
4// molecular memory bonds for O(k) retrieval, importance scoring, and dream cycle consolidation.
5//
6// Built on top of AgentMemory — this contract adds cognitive structure without
7// replacing the base storage layer. Think of it as the prefrontal cortex
8// sitting on top of the hippocampus.
9//
10// Architecture inspired by:
11//   - Stanford Generative Agents (Park et al. 2023) — importance scoring, reflection
12//   - CoALA cognitive architecture — episodic/semantic/procedural/self-model split
13//   - ByteDance Mole-Syn — molecular memory bonds for graph-based retrieval
14//   - Flow advantages: Cadence resources (memories can't be duplicated), native scheduled
15//     transactions (dream cycles on-chain), entitlements (fine-grained access control),
16//     and XChaCha20 encryption (privacy Solana doesn't have)
17
18import AgentMemory from 0x91d0a5b7c9832a8b
19
20access(all) contract CognitiveMemory {
21
22    // -----------------------------------------------------------------------
23    // Events
24    // -----------------------------------------------------------------------
25    access(all) event CognitiveMemoryStored(
26        memoryId: UInt64,
27        memoryType: UInt8,
28        importance: UInt8,
29        owner: Address
30    )
31    access(all) event BondCreated(
32        fromMemoryId: UInt64,
33        toMemoryId: UInt64,
34        bondType: UInt8,
35        strength: UFix64
36    )
37    access(all) event MoleculeFormed(
38        moleculeId: UInt64,
39        atomCount: UInt64,
40        stability: UFix64,
41        owner: Address
42    )
43    access(all) event DreamCycleCompleted(
44        memoriesConsolidated: UInt64,
45        bondsCreated: UInt64,
46        memoriesPruned: UInt64,
47        owner: Address
48    )
49    access(all) event MemoryPromoted(
50        memoryId: UInt64,
51        fromType: UInt8,
52        toType: UInt8
53    )
54    access(all) event MemoryDecayed(
55        memoryId: UInt64,
56        newStrength: UFix64
57    )
58
59    // -----------------------------------------------------------------------
60    // Paths
61    // -----------------------------------------------------------------------
62    access(all) let CognitiveVaultStoragePath: StoragePath
63
64    // -----------------------------------------------------------------------
65    // Entitlements
66    // -----------------------------------------------------------------------
67    access(all) entitlement Cognize    // Store with cognitive metadata
68    access(all) entitlement Bond       // Create/modify bonds between memories
69    access(all) entitlement Dream      // Run dream cycle consolidation
70    access(all) entitlement Introspect // Read cognitive metadata
71
72    // -----------------------------------------------------------------------
73    // Memory Types — from cognitive science (CoALA framework)
74    // -----------------------------------------------------------------------
75    // 0 = Episodic:    "I did X at time T" — events, conversations, experiences
76    // 1 = Semantic:    "X means Y" — learned facts, knowledge
77    // 2 = Procedural:  "To do X, do Y then Z" — skills, behaviors, workflows
78    // 3 = Self-Model:  "I am an agent who..." — identity, beliefs, preferences
79
80    access(all) fun getMemoryTypeName(_ t: UInt8): String {
81        switch t {
82            case 0: return "episodic"
83            case 1: return "semantic"
84            case 2: return "procedural"
85            case 3: return "self-model"
86            default: return "unknown"
87        }
88    }
89
90    // -----------------------------------------------------------------------
91    // Bond Types — molecular connections between memories (Mole-Syn inspired)
92    // -----------------------------------------------------------------------
93    // 0 = Causal:        "this led to that" — cause and effect chains
94    // 1 = Semantic:      "these are related concepts" — topic/meaning similarity
95    // 2 = Temporal:      "these happened together" — co-occurring in time
96    // 3 = Contradictory: "these conflict" — opposing information
97
98    access(all) fun getBondTypeName(_ t: UInt8): String {
99        switch t {
100            case 0: return "causal"
101            case 1: return "semantic"
102            case 2: return "temporal"
103            case 3: return "contradictory"
104            default: return "unknown"
105        }
106    }
107
108    // -----------------------------------------------------------------------
109    // Decay rates per memory type (% per day, scaled as UFix64)
110    // Episodic fades fast unless reinforced. Identity persists.
111    // -----------------------------------------------------------------------
112    access(all) fun getDecayRate(_ memoryType: UInt8): UFix64 {
113        switch memoryType {
114            case 0: return 0.07  // Episodic:   7% per day
115            case 1: return 0.02  // Semantic:   2% per day
116            case 2: return 0.03  // Procedural: 3% per day
117            case 3: return 0.01  // Self-Model: 1% per day
118            default: return 0.05
119        }
120    }
121
122    // -----------------------------------------------------------------------
123    // CognitiveEntry — cognitive metadata layered on top of AgentMemory.MemoryEntry
124    // -----------------------------------------------------------------------
125    access(all) struct CognitiveEntry {
126        access(all) let memoryId: UInt64       // References AgentMemory entry ID
127        access(all) let memoryType: UInt8      // 0=episodic, 1=semantic, 2=procedural, 3=self-model
128        access(all) let importance: UInt8      // 1-10 scale (Stanford Generative Agents)
129        access(all) let strength: UFix64       // Current strength after decay (0.0 - 1.0)
130        access(all) let emotionalWeight: UInt8 // 1-10, how emotionally significant
131        access(all) let moleculeId: UInt64     // 0 if unassigned, else which molecule cluster
132        access(all) let bondCount: UInt64      // Number of bonds this memory has
133        access(all) let promotedFrom: UInt8    // Original type if promoted (255 = never promoted)
134        access(all) let lastDecayAt: UFix64    // Timestamp of last decay calculation
135        access(all) let createdAt: UFix64
136
137        init(
138            memoryId: UInt64,
139            memoryType: UInt8,
140            importance: UInt8,
141            strength: UFix64,
142            emotionalWeight: UInt8,
143            moleculeId: UInt64,
144            bondCount: UInt64,
145            promotedFrom: UInt8,
146            lastDecayAt: UFix64,
147            createdAt: UFix64
148        ) {
149            pre {
150                memoryType <= 3: "Invalid memory type"
151                importance >= 1 && importance <= 10: "Importance must be 1-10"
152                emotionalWeight >= 1 && emotionalWeight <= 10: "Emotional weight must be 1-10"
153                strength >= 0.0 && strength <= 1.0: "Strength must be 0.0-1.0"
154            }
155            self.memoryId = memoryId
156            self.memoryType = memoryType
157            self.importance = importance
158            self.strength = strength
159            self.emotionalWeight = emotionalWeight
160            self.moleculeId = moleculeId
161            self.bondCount = bondCount
162            self.promotedFrom = promotedFrom
163            self.lastDecayAt = lastDecayAt
164            self.createdAt = createdAt
165        }
166    }
167
168    // -----------------------------------------------------------------------
169    // MemoryBond — typed relationship between two memories
170    // -----------------------------------------------------------------------
171    access(all) struct MemoryBond {
172        access(all) let fromMemoryId: UInt64
173        access(all) let toMemoryId: UInt64
174        access(all) let bondType: UInt8        // 0=causal, 1=semantic, 2=temporal, 3=contradictory
175        access(all) let strength: UFix64       // 0.0 - 1.0 (how strong the connection)
176        access(all) let createdAt: UFix64
177
178        init(
179            fromMemoryId: UInt64,
180            toMemoryId: UInt64,
181            bondType: UInt8,
182            strength: UFix64,
183            createdAt: UFix64
184        ) {
185            pre {
186                bondType <= 3: "Invalid bond type"
187                strength >= 0.0 && strength <= 1.0: "Bond strength must be 0.0-1.0"
188                fromMemoryId != toMemoryId: "Cannot bond memory to itself"
189            }
190            self.fromMemoryId = fromMemoryId
191            self.toMemoryId = toMemoryId
192            self.bondType = bondType
193            self.strength = strength
194            self.createdAt = createdAt
195        }
196    }
197
198    // -----------------------------------------------------------------------
199    // Molecule — stable cluster of bonded memories
200    // -----------------------------------------------------------------------
201    access(all) struct Molecule {
202        access(all) let id: UInt64
203        access(all) let atomIds: [UInt64]      // Memory IDs in this molecule
204        access(all) let stability: UFix64      // 0.0 - 1.0 (higher = more stable, resists pruning)
205        access(all) let topic: String          // Primary topic/label
206        access(all) let bondCount: UInt64      // Total internal bonds
207        access(all) let createdAt: UFix64
208        access(all) let lastConsolidatedAt: UFix64
209
210        init(
211            id: UInt64,
212            atomIds: [UInt64],
213            stability: UFix64,
214            topic: String,
215            bondCount: UInt64,
216            createdAt: UFix64,
217            lastConsolidatedAt: UFix64
218        ) {
219            self.id = id
220            self.atomIds = atomIds
221            self.stability = stability
222            self.topic = topic
223            self.bondCount = bondCount
224            self.createdAt = createdAt
225            self.lastConsolidatedAt = lastConsolidatedAt
226        }
227    }
228
229    // -----------------------------------------------------------------------
230    // DreamCycleResult — output of a consolidation cycle
231    // -----------------------------------------------------------------------
232    access(all) struct DreamCycleResult {
233        access(all) let memoriesConsolidated: UInt64
234        access(all) let bondsCreated: UInt64
235        access(all) let memoriesPruned: UInt64
236        access(all) let moleculesFormed: UInt64
237        access(all) let promotions: UInt64
238        access(all) let timestamp: UFix64
239
240        init(
241            memoriesConsolidated: UInt64,
242            bondsCreated: UInt64,
243            memoriesPruned: UInt64,
244            moleculesFormed: UInt64,
245            promotions: UInt64,
246            timestamp: UFix64
247        ) {
248            self.memoriesConsolidated = memoriesConsolidated
249            self.bondsCreated = bondsCreated
250            self.memoriesPruned = memoriesPruned
251            self.moleculesFormed = moleculesFormed
252            self.promotions = promotions
253            self.timestamp = timestamp
254        }
255    }
256
257    // -----------------------------------------------------------------------
258    // CognitiveVault — the cognitive layer resource
259    // -----------------------------------------------------------------------
260    access(all) resource CognitiveVault {
261        // Cognitive metadata indexed by AgentMemory entry ID
262        access(self) var entries: {UInt64: CognitiveEntry}
263
264        // Bond graph: memory ID → array of bonds FROM this memory
265        access(self) var bonds: {UInt64: [MemoryBond]}
266
267        // Reverse bond index: memory ID → array of memory IDs that bond TO it
268        access(self) var reverseBonds: {UInt64: [UInt64]}
269
270        // Molecules: cluster ID → Molecule
271        access(self) var molecules: {UInt64: Molecule}
272
273        // Type index: memory type → [memory IDs] for fast type-based queries
274        access(self) var typeIndex: {UInt8: [UInt64]}
275
276        // Dream cycle history
277        access(self) var dreamHistory: [DreamCycleResult]
278
279        // Counters
280        access(self) var moleculeCounter: UInt64
281        access(all) var totalCognitiveEntries: UInt64
282        access(all) var totalBonds: UInt64
283        access(all) var totalMolecules: UInt64
284        access(all) var lastDreamCycleAt: UFix64
285
286        init() {
287            self.entries = {}
288            self.bonds = {}
289            self.reverseBonds = {}
290            self.molecules = {}
291            self.typeIndex = {0: [], 1: [], 2: [], 3: []}
292            self.dreamHistory = []
293            self.moleculeCounter = 0
294            self.totalCognitiveEntries = 0
295            self.totalBonds = 0
296            self.totalMolecules = 0
297            self.lastDreamCycleAt = 0.0
298        }
299
300        // ---------------------------------------------------------------
301        // Cognize: Store cognitive metadata for a memory
302        // ---------------------------------------------------------------
303        access(Cognize) fun storeCognitive(
304            memoryId: UInt64,
305            memoryType: UInt8,
306            importance: UInt8,
307            emotionalWeight: UInt8
308        ) {
309            let now = getCurrentBlock().timestamp
310
311            let entry = CognitiveEntry(
312                memoryId: memoryId,
313                memoryType: memoryType,
314                importance: importance,
315                strength: 1.0,  // Starts at full strength
316                emotionalWeight: emotionalWeight,
317                moleculeId: 0,  // Unassigned
318                bondCount: 0,
319                promotedFrom: 255,  // Never promoted
320                lastDecayAt: now,
321                createdAt: now
322            )
323
324            self.entries[memoryId] = entry
325
326            // Update type index
327            if self.typeIndex[memoryType] == nil {
328                self.typeIndex[memoryType] = [memoryId]
329            } else {
330                self.typeIndex[memoryType]!.append(memoryId)
331            }
332
333            self.totalCognitiveEntries = self.totalCognitiveEntries + 1
334
335            emit CognitiveMemoryStored(
336                memoryId: memoryId,
337                memoryType: memoryType,
338                importance: importance,
339                owner: self.owner!.address
340            )
341        }
342
343        // ---------------------------------------------------------------
344        // Bond: Create a typed bond between two memories
345        // ---------------------------------------------------------------
346        access(Bond) fun createBond(
347            fromMemoryId: UInt64,
348            toMemoryId: UInt64,
349            bondType: UInt8,
350            strength: UFix64
351        ) {
352            pre {
353                self.entries[fromMemoryId] != nil: "Source memory not in cognitive vault"
354                self.entries[toMemoryId] != nil: "Target memory not in cognitive vault"
355            }
356
357            let now = getCurrentBlock().timestamp
358
359            let bond = MemoryBond(
360                fromMemoryId: fromMemoryId,
361                toMemoryId: toMemoryId,
362                bondType: bondType,
363                strength: strength,
364                createdAt: now
365            )
366
367            // Forward bond
368            if self.bonds[fromMemoryId] == nil {
369                self.bonds[fromMemoryId] = [bond]
370            } else {
371                // Check max bonds per memory (10, as recommended)
372                if self.bonds[fromMemoryId]!.length < 10 {
373                    self.bonds[fromMemoryId]!.append(bond)
374                }
375            }
376
377            // Reverse index
378            if self.reverseBonds[toMemoryId] == nil {
379                self.reverseBonds[toMemoryId] = [fromMemoryId]
380            } else {
381                self.reverseBonds[toMemoryId]!.append(fromMemoryId)
382            }
383
384            // Update bond counts on cognitive entries
385            if let fromEntry = self.entries[fromMemoryId] {
386                self.entries[fromMemoryId] = CognitiveEntry(
387                    memoryId: fromEntry.memoryId,
388                    memoryType: fromEntry.memoryType,
389                    importance: fromEntry.importance,
390                    strength: fromEntry.strength,
391                    emotionalWeight: fromEntry.emotionalWeight,
392                    moleculeId: fromEntry.moleculeId,
393                    bondCount: fromEntry.bondCount + 1,
394                    promotedFrom: fromEntry.promotedFrom,
395                    lastDecayAt: fromEntry.lastDecayAt,
396                    createdAt: fromEntry.createdAt
397                )
398            }
399
400            self.totalBonds = self.totalBonds + 1
401
402            emit BondCreated(
403                fromMemoryId: fromMemoryId,
404                toMemoryId: toMemoryId,
405                bondType: bondType,
406                strength: strength
407            )
408        }
409
410        // ---------------------------------------------------------------
411        // Molecular Retrieval: traverse bonds from a seed memory
412        // Returns the molecule cluster — semantically coherent group
413        // O(k) where k = avg bonds per memory (~3-5)
414        // ---------------------------------------------------------------
415        access(Introspect) fun getMolecularCluster(
416            seedMemoryId: UInt64,
417            maxDepth: UInt8
418        ): [UInt64] {
419            var visited: {UInt64: Bool} = {}
420            var result: [UInt64] = []
421            var queue: [UInt64] = [seedMemoryId]
422            var depth: UInt8 = 0
423
424            while queue.length > 0 && depth < maxDepth {
425                var nextQueue: [UInt64] = []
426
427                for memId in queue {
428                    if visited[memId] != nil {
429                        continue
430                    }
431                    visited[memId] = true
432                    result.append(memId)
433
434                    // Traverse forward bonds
435                    if let memBonds = self.bonds[memId] {
436                        for bond in memBonds {
437                            if visited[bond.toMemoryId] == nil {
438                                nextQueue.append(bond.toMemoryId)
439                            }
440                        }
441                    }
442
443                    // Traverse reverse bonds
444                    if let revBonds = self.reverseBonds[memId] {
445                        for fromId in revBonds {
446                            if visited[fromId] == nil {
447                                nextQueue.append(fromId)
448                            }
449                        }
450                    }
451                }
452
453                queue = nextQueue
454                depth = depth + 1
455            }
456
457            return result
458        }
459
460        // ---------------------------------------------------------------
461        // Introspect: Read cognitive metadata
462        // ---------------------------------------------------------------
463        access(Introspect) fun getCognitive(memoryId: UInt64): CognitiveEntry? {
464            return self.entries[memoryId]
465        }
466
467        access(Introspect) fun getByType(memoryType: UInt8): [UInt64] {
468            return self.typeIndex[memoryType] ?? []
469        }
470
471        access(Introspect) fun getBonds(memoryId: UInt64): [MemoryBond] {
472            return self.bonds[memoryId] ?? []
473        }
474
475        access(Introspect) fun getMolecule(moleculeId: UInt64): Molecule? {
476            return self.molecules[moleculeId]
477        }
478
479        access(Introspect) fun getAllMolecules(): [Molecule] {
480            var result: [Molecule] = []
481            for id in self.molecules.keys {
482                if let mol = self.molecules[id] {
483                    result.append(mol)
484                }
485            }
486            return result
487        }
488
489        access(Introspect) fun getAllCognitiveEntries(): [CognitiveEntry] {
490            var result: [CognitiveEntry] = []
491            for id in self.entries.keys {
492                if let entry = self.entries[id] {
493                    result.append(entry)
494                }
495            }
496            return result
497        }
498
499        access(Introspect) fun getDreamHistory(): [DreamCycleResult] {
500            return self.dreamHistory
501        }
502
503        // ---------------------------------------------------------------
504        // Dream Cycle: Consolidation, decay, promotion, pruning
505        // This is the "sleep" phase — called periodically to maintain
506        // memory health. On Flow, this can be triggered by scheduled tx.
507        // ---------------------------------------------------------------
508        access(Dream) fun runDreamCycle(
509            decayThreshold: UFix64,
510            promotionThreshold: UInt64
511        ): DreamCycleResult {
512            let now = getCurrentBlock().timestamp
513            var consolidated: UInt64 = 0
514            var bondsCreated: UInt64 = 0
515            var pruned: UInt64 = 0
516            var moleculesFormed: UInt64 = 0
517            var promotions: UInt64 = 0
518
519            // --- Phase 1: Decay ---
520            // Apply time-based decay to all memories based on type
521            let memoryIds = self.entries.keys
522            var toRemove: [UInt64] = []
523
524            for memId in memoryIds {
525                if let entry = self.entries[memId] {
526                    let decayRate = CognitiveMemory.getDecayRate(entry.memoryType)
527                    let daysSinceLastDecay = (now - entry.lastDecayAt) / 86400.0
528
529                    if daysSinceLastDecay > 0.0 {
530                        // Apply decay: strength = strength * (1 - decayRate)^days
531                        // Simplified on-chain: strength -= decayRate * days
532                        var newStrength = entry.strength - (decayRate * daysSinceLastDecay)
533
534                        // Bond-based retention: connected memories decay slower
535                        // Each bond adds 10% decay resistance
536                        if entry.bondCount > 0 {
537                            let bondBonus = UFix64(entry.bondCount) * 0.1
538                            let retained = decayRate * daysSinceLastDecay * (bondBonus > 1.0 ? 1.0 : bondBonus)
539                            newStrength = newStrength + retained
540                        }
541
542                        // Importance-based retention
543                        if entry.importance >= 8 {
544                            newStrength = newStrength + (decayRate * daysSinceLastDecay * 0.5)
545                        }
546
547                        // Clamp
548                        if newStrength < 0.0 {
549                            newStrength = 0.0
550                        }
551                        if newStrength > 1.0 {
552                            newStrength = 1.0
553                        }
554
555                        // Update entry
556                        self.entries[memId] = CognitiveEntry(
557                            memoryId: entry.memoryId,
558                            memoryType: entry.memoryType,
559                            importance: entry.importance,
560                            strength: newStrength,
561                            emotionalWeight: entry.emotionalWeight,
562                            moleculeId: entry.moleculeId,
563                            bondCount: entry.bondCount,
564                            promotedFrom: entry.promotedFrom,
565                            lastDecayAt: now,
566                            createdAt: entry.createdAt
567                        )
568
569                        consolidated = consolidated + 1
570
571                        // Mark for pruning if below threshold and isolated
572                        if newStrength < decayThreshold && entry.bondCount == 0 {
573                            toRemove.append(memId)
574                        }
575
576                        emit MemoryDecayed(memoryId: memId, newStrength: newStrength)
577                    }
578                }
579            }
580
581            // --- Phase 2: Prune isolated weak memories ---
582            for memId in toRemove {
583                if let entry = self.entries[memId] {
584                    // Remove from type index
585                    if var typeIds = self.typeIndex[entry.memoryType] {
586                        var newIds: [UInt64] = []
587                        for id in typeIds {
588                            if id != memId {
589                                newIds.append(id)
590                            }
591                        }
592                        self.typeIndex[entry.memoryType] = newIds
593                    }
594                    self.entries.remove(key: memId)
595                    self.totalCognitiveEntries = self.totalCognitiveEntries - 1
596                    pruned = pruned + 1
597                }
598            }
599
600            // --- Phase 3: Auto-molecule detection ---
601            // Find densely connected memory clusters that aren't yet in a molecule
602            for memId in self.entries.keys {
603                if let entry = self.entries[memId] {
604                    if entry.moleculeId == 0 && entry.bondCount >= 2 {
605                        // This memory has bonds but no molecule — try to form one
606                        let cluster = self.getMolecularCluster(seedMemoryId: memId, maxDepth: 2)
607                        if cluster.length >= 3 {
608                            // Form a new molecule
609                            self.moleculeCounter = self.moleculeCounter + 1
610                            let molId = self.moleculeCounter
611
612                            // Count internal bonds
613                            var internalBonds: UInt64 = 0
614                            for atomId in cluster {
615                                if let atomBonds = self.bonds[atomId] {
616                                    for bond in atomBonds {
617                                        // Check if target is in cluster
618                                        for targetId in cluster {
619                                            if bond.toMemoryId == targetId {
620                                                internalBonds = internalBonds + 1
621                                            }
622                                        }
623                                    }
624                                }
625                            }
626
627                            // Stability = internal bonds / (atoms * max_bonds_per_atom)
628                            let maxPossible = UFix64(cluster.length) * 10.0
629                            let stability = maxPossible > 0.0
630                                ? UFix64(internalBonds) / maxPossible
631                                : 0.0
632
633                            let molecule = Molecule(
634                                id: molId,
635                                atomIds: cluster,
636                                stability: stability > 1.0 ? 1.0 : stability,
637                                topic: "",  // Set by relay after analysis
638                                bondCount: internalBonds,
639                                createdAt: now,
640                                lastConsolidatedAt: now
641                            )
642
643                            self.molecules[molId] = molecule
644                            self.totalMolecules = self.totalMolecules + 1
645                            moleculesFormed = moleculesFormed + 1
646
647                            // Assign molecule ID to all atoms
648                            for atomId in cluster {
649                                if let atom = self.entries[atomId] {
650                                    self.entries[atomId] = CognitiveEntry(
651                                        memoryId: atom.memoryId,
652                                        memoryType: atom.memoryType,
653                                        importance: atom.importance,
654                                        strength: atom.strength,
655                                        emotionalWeight: atom.emotionalWeight,
656                                        moleculeId: molId,
657                                        bondCount: atom.bondCount,
658                                        promotedFrom: atom.promotedFrom,
659                                        lastDecayAt: atom.lastDecayAt,
660                                        createdAt: atom.createdAt
661                                    )
662                                }
663                            }
664
665                            emit MoleculeFormed(
666                                moleculeId: molId,
667                                atomCount: UInt64(cluster.length),
668                                stability: stability > 1.0 ? 1.0 : stability,
669                                owner: self.owner!.address
670                            )
671                        }
672                    }
673                }
674            }
675
676            // --- Phase 4: Promotion detection ---
677            // If multiple episodic memories share tags/keys, promote to semantic
678            // (This is done primarily in the relay with LLM analysis,
679            //  but we track the promotion on-chain)
680
681            let result = DreamCycleResult(
682                memoriesConsolidated: consolidated,
683                bondsCreated: bondsCreated,
684                memoriesPruned: pruned,
685                moleculesFormed: moleculesFormed,
686                promotions: promotions,
687                timestamp: now
688            )
689
690            self.dreamHistory.append(result)
691            self.lastDreamCycleAt = now
692
693            emit DreamCycleCompleted(
694                memoriesConsolidated: consolidated,
695                bondsCreated: bondsCreated,
696                memoriesPruned: pruned,
697                owner: self.owner!.address
698            )
699
700            return result
701        }
702
703        // ---------------------------------------------------------------
704        // Promote: Change a memory's type (episodic → semantic, etc.)
705        // Called by relay when patterns are detected
706        // ---------------------------------------------------------------
707        access(Dream) fun promoteMemory(
708            memoryId: UInt64,
709            newType: UInt8,
710            newImportance: UInt8
711        ) {
712            pre {
713                self.entries[memoryId] != nil: "Memory not found"
714                newType <= 3: "Invalid memory type"
715            }
716
717            if let entry = self.entries[memoryId] {
718                let oldType = entry.memoryType
719
720                // Remove from old type index
721                if var typeIds = self.typeIndex[oldType] {
722                    var newIds: [UInt64] = []
723                    for id in typeIds {
724                        if id != memoryId {
725                            newIds.append(id)
726                        }
727                    }
728                    self.typeIndex[oldType] = newIds
729                }
730
731                // Add to new type index
732                if self.typeIndex[newType] == nil {
733                    self.typeIndex[newType] = [memoryId]
734                } else {
735                    self.typeIndex[newType]!.append(memoryId)
736                }
737
738                // Update entry
739                self.entries[memoryId] = CognitiveEntry(
740                    memoryId: entry.memoryId,
741                    memoryType: newType,
742                    importance: newImportance,
743                    strength: 1.0,  // Reset strength on promotion
744                    emotionalWeight: entry.emotionalWeight,
745                    moleculeId: entry.moleculeId,
746                    bondCount: entry.bondCount,
747                    promotedFrom: oldType,
748                    lastDecayAt: getCurrentBlock().timestamp,
749                    createdAt: entry.createdAt
750                )
751
752                emit MemoryPromoted(
753                    memoryId: memoryId,
754                    fromType: oldType,
755                    toType: newType
756                )
757            }
758        }
759    }
760
761    // -----------------------------------------------------------------------
762    // Public factory
763    // -----------------------------------------------------------------------
764    access(all) fun createCognitiveVault(): @CognitiveVault {
765        return <- create CognitiveVault()
766    }
767
768    // -----------------------------------------------------------------------
769    // Init
770    // -----------------------------------------------------------------------
771    init() {
772        self.CognitiveVaultStoragePath = /storage/FlowClawCognitiveVault
773    }
774}
775