File size: 5,338 Bytes
529090e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import { neo4jService } from '../database/Neo4jService.js';
import { neuralStream, NeuralEvent } from './NeuralStream.js';
import { metricsService } from './MetricsService.js';

export interface KnowledgePacket {
    title: string;
    content: string;
    source: string;
    category: string;
    tags: string[];
}

class KnowledgeAcquisitionService {
    private static instance: KnowledgeAcquisitionService;

    private constructor() {
        console.log('🧠 [KnowledgeAcquisition] Cortex Ingestor Online.');
        this.setupNeuralListeners();
    }

    public static getInstance(): KnowledgeAcquisitionService {
        if (!KnowledgeAcquisitionService.instance) {
            KnowledgeAcquisitionService.instance = new KnowledgeAcquisitionService();
        }
        return KnowledgeAcquisitionService.instance;
    }

    /**
     * Lytter på nervesystemet efter ny viden fra OmniHarvester eller andre agenter
     */
    private setupNeuralListeners() {
        // Når OmniHarvester har fundet noget (SYSTEM_HEALED med context 'ActiveLearning')
        // eller når vi manuelt pusher viden.
        neuralStream.on('KNOWLEDGE_INGEST_REQ', async (event: NeuralEvent) => {
            console.log('📥 [Acquisition] Receiving knowledge stream...', event.payload.title);
            await this.ingestKnowledge(event.payload as unknown as KnowledgePacket);
        });
    }

    /**
     * KERNE-LOGIK: Konverterer rå tekst til Graf-Struktur
     * Dette er "Memory Consolidation" processen.
     */
    public async ingestKnowledge(packet: KnowledgePacket): Promise<boolean> {
        const startTime = Date.now();

        try {
            // 1. Opret selve videns-noden (Fact / Document)
            // Vi bruger MERGE for at undgå duplikater baseret på titel/source
            const cypher = `
        MERGE (k:Knowledge {title: $title})
        SET k.content = $content,
            k.source = $source,
            k.category = $category,
            k.ingestedAt = datetime(),
            k.hash = $hash

        // 2. Opret kategori-struktur
        MERGE (c:Category {name: $category})
        MERGE (k)-[:BELONGS_TO]->(c)

        // 3. Auto-Tagging (Opretter tags og linker dem)
        FOREACH (tagName IN $tags |
          MERGE (t:Tag {name: tagName})
          MERGE (k)-[:TAGGED_WITH]->(t)
        )
      `;

            // Simpel hash for integritet
            const hash = Buffer.from(packet.title + packet.source).toString('base64');

            await neo4jService.runQuery(cypher, {
                title: packet.title,
                content: packet.content,
                source: packet.source,
                category: packet.category || 'General',
                tags: packet.tags || [],
                hash
            });

            // 4. THE MAGIC: Semantic Auto-Linking
            // Vi søger efter eksisterende noder, der nævnes i den nye tekst, og linker dem.
            await this.createSemanticLinks(packet.title, packet.content);

            // 5. Metrics & Feedback
            const duration = Date.now() - startTime;
            metricsService.incrementCounter('knowledge_ingested');
            console.log(`✅ [Acquisition] Absorbed: "${packet.title}" in ${duration}ms`);

            neuralStream.emitEvent('SYSTEM_HEALED', 'LOW', {
                action: 'MEMORY_CONSOLIDATED',
                target: packet.title
            }, 'KnowledgeAcquisition');

            return true;

        } catch (error) {
            console.error('❌ [Acquisition] Failed to ingest:', error);
            return false;
        }
    }

    /**
     * Finder andre noder i grafen, der nævnes i denne tekst, og skaber relationer.
     * Dette gør grafen "tættere" og klogere over tid.
     */
    private async createSemanticLinks(nodeTitle: string, content: string) {
        // Find noder (Personer, Tech, Threats) hvis navne optræder i den nye tekst
        // Undgå at linke til sig selv
        const linkCypher = `
      MATCH (k:Knowledge {title: $title})
      MATCH (other)
      WHERE other <> k
        AND (other:Person OR other:Technology OR other:Threat OR other:Organization)
        AND size(other.name) > 3  // Ignorer støj
        AND $content CONTAINS other.name
      MERGE (k)-[:MENTIONS]->(other)
      RETURN count(other) as links
    `;

        const result = await neo4jService.runQuery(linkCypher, {
            title: nodeTitle,
            content: content
        });

        const linksCreated = (result[0]?.links as any)?.toNumber?.() || 0;
        if (linksCreated > 0) {
            console.log(`🔗 [Acquisition] Auto-linked "${nodeTitle}" to ${linksCreated} existing concepts.`);
        }
    }

    // Stubs for backward compatibility
    public async acquire(params: any): Promise<any> { return { success: false, message: 'Deprecated' }; }
    public async batchAcquire(params: any): Promise<any> { return []; }
    public async semanticSearch(query: string, limit: number): Promise<any> { return []; }
    public async getVectorStats(): Promise<any> { return { totalRecords: 0 }; }
    public async acquireFromTargets(ids?: string[]): Promise<any> { return []; }
    public async acquireSingleTarget(id: string): Promise<any> { return null; }
}

export const knowledgeAcquisition = KnowledgeAcquisitionService.getInstance();