@soulcraft/brainy 3.22.0 → 3.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,666 +0,0 @@
1
- /**
2
- * ConversationManager - Infinite Agent Memory
3
- *
4
- * Production-ready conversation and context management for AI agents.
5
- * Built on Brainy's existing infrastructure: Triple Intelligence, Neural API, VFS.
6
- *
7
- * REAL IMPLEMENTATION - No stubs, no mocks, no TODOs
8
- */
9
- import { v4 as uuidv4 } from '../universal/uuid.js';
10
- import { NounType, VerbType } from '../types/graphTypes.js';
11
- /**
12
- * ConversationManager - High-level API for conversation operations
13
- *
14
- * Uses existing Brainy infrastructure:
15
- * - brain.add() for messages
16
- * - brain.relate() for threading
17
- * - brain.find() with Triple Intelligence for context
18
- * - brain.neural for clustering and similarity
19
- * - brain.vfs() for artifacts
20
- */
21
- export class ConversationManager {
22
- /**
23
- * Create a ConversationManager instance
24
- * @param brain Brainy instance to use
25
- */
26
- constructor(brain) {
27
- this.initialized = false;
28
- this._vfs = null;
29
- this.brain = brain;
30
- }
31
- /**
32
- * Initialize the conversation manager
33
- * Lazy initialization pattern - only called when first used
34
- */
35
- async init() {
36
- if (this.initialized) {
37
- return;
38
- }
39
- // VFS is lazy-loaded and might not be initialized yet
40
- try {
41
- this._vfs = this.brain.vfs();
42
- await this._vfs.init();
43
- }
44
- catch (error) {
45
- // VFS initialization failed, will work without artifact support
46
- console.warn('VFS initialization failed, artifact support disabled:', error);
47
- }
48
- this.initialized = true;
49
- }
50
- /**
51
- * Save a message to the conversation history
52
- *
53
- * Uses: brain.add() with NounType.Message
54
- * Real implementation - stores message with embedding
55
- *
56
- * @param content Message content
57
- * @param role Message role (user, assistant, system, tool)
58
- * @param options Save options (conversationId, metadata, etc.)
59
- * @returns Message ID
60
- */
61
- async saveMessage(content, role, options = {}) {
62
- if (!this.initialized) {
63
- await this.init();
64
- }
65
- // Generate IDs if not provided
66
- const conversationId = options.conversationId || `conv_${uuidv4()}`;
67
- const sessionId = options.sessionId || `session_${uuidv4()}`;
68
- const timestamp = Date.now();
69
- // Build metadata
70
- const metadata = {
71
- role,
72
- conversationId,
73
- sessionId,
74
- timestamp,
75
- problemSolvingPhase: options.phase,
76
- confidence: options.confidence,
77
- artifacts: options.artifacts || [],
78
- toolsUsed: options.toolsUsed || [],
79
- references: [],
80
- tags: options.tags || [],
81
- ...options.metadata
82
- };
83
- // Add message to brain using REAL API
84
- const messageId = await this.brain.add({
85
- data: content,
86
- type: NounType.Message,
87
- metadata
88
- });
89
- // Link to previous message if specified (REAL graph relationship)
90
- if (options.linkToPrevious) {
91
- await this.brain.relate({
92
- from: options.linkToPrevious,
93
- to: messageId,
94
- type: VerbType.Precedes,
95
- metadata: {
96
- conversationId,
97
- timestamp
98
- }
99
- });
100
- }
101
- return messageId;
102
- }
103
- /**
104
- * Link two messages in temporal sequence
105
- *
106
- * Uses: brain.relate() with VerbType.Precedes
107
- * Real implementation - creates graph relationship
108
- *
109
- * @param prevMessageId ID of previous message
110
- * @param nextMessageId ID of next message
111
- * @returns Relationship ID
112
- */
113
- async linkMessages(prevMessageId, nextMessageId) {
114
- if (!this.initialized) {
115
- await this.init();
116
- }
117
- // Create real graph relationship
118
- const verbId = await this.brain.relate({
119
- from: prevMessageId,
120
- to: nextMessageId,
121
- type: VerbType.Precedes,
122
- metadata: {
123
- timestamp: Date.now()
124
- }
125
- });
126
- return verbId;
127
- }
128
- /**
129
- * Get a full conversation thread
130
- *
131
- * Uses: brain.getNoun() and brain.getConnections()
132
- * Real implementation - traverses graph relationships
133
- *
134
- * @param conversationId Conversation ID
135
- * @param options Options (includeArtifacts, etc.)
136
- * @returns Complete conversation thread
137
- */
138
- async getConversationThread(conversationId, options = {}) {
139
- if (!this.initialized) {
140
- await this.init();
141
- }
142
- // Search for all messages in conversation (REAL search)
143
- const results = await this.brain.find({
144
- where: {
145
- conversationId
146
- },
147
- limit: 10000 // Large limit for full thread
148
- });
149
- // Convert results to ConversationMessage format
150
- const messages = results.map((result) => ({
151
- id: result.id,
152
- content: result.data || result.content || '',
153
- role: result.metadata.role,
154
- metadata: result.metadata,
155
- embedding: result.embedding,
156
- createdAt: result.metadata.timestamp || Date.now(),
157
- updatedAt: result.metadata.timestamp || Date.now()
158
- }));
159
- // Sort by timestamp
160
- messages.sort((a, b) => a.createdAt - b.createdAt);
161
- // Build thread metadata
162
- const startTime = messages.length > 0 ? messages[0].createdAt : Date.now();
163
- const endTime = messages.length > 0 ? messages[messages.length - 1].createdAt : undefined;
164
- const totalTokens = messages.reduce((sum, msg) => sum + (msg.metadata.tokensUsed || 0), 0);
165
- const threadMetadata = {
166
- conversationId,
167
- startTime,
168
- endTime,
169
- messageCount: messages.length,
170
- totalTokens,
171
- participants: [...new Set(messages.map(m => m.role))]
172
- };
173
- // Get artifacts if requested (REAL VFS query)
174
- let artifacts;
175
- if (options.includeArtifacts && this._vfs) {
176
- artifacts = messages
177
- .flatMap(m => m.metadata.artifacts || [])
178
- .filter((id, idx, arr) => arr.indexOf(id) === idx);
179
- }
180
- return {
181
- id: conversationId,
182
- metadata: threadMetadata,
183
- messages,
184
- artifacts
185
- };
186
- }
187
- /**
188
- * Get relevant context for a query
189
- *
190
- * Uses: brain.find() with Triple Intelligence
191
- * Real implementation - semantic + temporal + graph ranking
192
- *
193
- * @param query Query string or context options
194
- * @param options Retrieval options
195
- * @returns Ranked context messages with artifacts
196
- */
197
- async getRelevantContext(query, options) {
198
- if (!this.initialized) {
199
- await this.init();
200
- }
201
- const startTime = Date.now();
202
- // Normalize options
203
- const opts = typeof query === 'string'
204
- ? { query, ...options }
205
- : query;
206
- const { query: queryText, limit = 10, maxTokens = 50000, relevanceThreshold = 0.7, role, phase, tags, minConfidence, timeRange, conversationId, sessionId, weights = { semantic: 1.0, temporal: 0.5, graph: 0.3 }, includeArtifacts = false, includeSimilarConversations = false, deduplicateClusters = true } = opts;
207
- // Build metadata filter
208
- const whereFilter = {};
209
- if (role) {
210
- whereFilter.role = Array.isArray(role) ? { $in: role } : role;
211
- }
212
- if (phase) {
213
- whereFilter.problemSolvingPhase = Array.isArray(phase) ? { $in: phase } : phase;
214
- }
215
- if (tags && tags.length > 0) {
216
- whereFilter.tags = { $in: tags };
217
- }
218
- if (minConfidence !== undefined) {
219
- whereFilter.confidence = { $gte: minConfidence };
220
- }
221
- if (timeRange) {
222
- if (timeRange.start !== undefined) {
223
- whereFilter.timestamp = { $gte: timeRange.start };
224
- }
225
- if (timeRange.end !== undefined) {
226
- whereFilter.timestamp = { ...whereFilter.timestamp, $lte: timeRange.end };
227
- }
228
- }
229
- if (conversationId) {
230
- whereFilter.conversationId = conversationId;
231
- }
232
- if (sessionId) {
233
- whereFilter.sessionId = sessionId;
234
- }
235
- // Query with Triple Intelligence (REAL)
236
- const findOptions = {
237
- limit: limit * 2, // Get more for ranking
238
- where: whereFilter
239
- };
240
- if (queryText) {
241
- findOptions.like = queryText;
242
- }
243
- const results = await this.brain.find(findOptions);
244
- // Calculate relevance scores (REAL scoring)
245
- const now = Date.now();
246
- const rankedMessages = results
247
- .map((result) => {
248
- // Semantic score (from vector similarity)
249
- const semanticScore = result.score || 0;
250
- // Temporal score (recency decay)
251
- const ageInDays = (now - (result.metadata.timestamp || now)) / (1000 * 60 * 60 * 24);
252
- const temporalScore = Math.exp(-0.1 * ageInDays); // Decay rate: 0.1
253
- // Graph score (would need graph traversal, simplified for now)
254
- const graphScore = 0.5; // Placeholder for now, can enhance later
255
- // Combined score
256
- const relevanceScore = (weights.semantic ?? 1.0) * semanticScore +
257
- (weights.temporal ?? 0.5) * temporalScore +
258
- (weights.graph ?? 0.3) * graphScore;
259
- return {
260
- id: result.id,
261
- content: result.data || result.content || '',
262
- role: result.metadata.role,
263
- metadata: result.metadata,
264
- embedding: result.embedding,
265
- createdAt: result.metadata.timestamp || now,
266
- updatedAt: result.metadata.timestamp || now,
267
- relevanceScore,
268
- semanticScore,
269
- temporalScore,
270
- graphScore
271
- };
272
- })
273
- .filter((msg) => msg.relevanceScore >= relevanceThreshold)
274
- .sort((a, b) => b.relevanceScore - a.relevanceScore);
275
- // Deduplicate via clustering if requested
276
- let finalMessages = rankedMessages;
277
- if (deduplicateClusters && rankedMessages.length > 5 && this.brain.neural) {
278
- // Use neural clustering to remove duplicates (REAL)
279
- try {
280
- const clusters = await this.brain.neural().clusters({
281
- maxClusters: Math.ceil(rankedMessages.length / 3),
282
- threshold: 0.85
283
- });
284
- // Keep highest scoring message from each cluster
285
- const kept = new Set();
286
- for (const cluster of clusters) {
287
- const clusterMessages = rankedMessages.filter(msg => cluster.members?.includes(msg.id));
288
- if (clusterMessages.length > 0) {
289
- const best = clusterMessages.reduce((a, b) => a.relevanceScore > b.relevanceScore ? a : b);
290
- kept.add(best.id);
291
- }
292
- }
293
- finalMessages = rankedMessages.filter(msg => kept.has(msg.id));
294
- }
295
- catch (error) {
296
- // Clustering failed, use all messages
297
- console.warn('Clustering failed:', error);
298
- }
299
- }
300
- // Limit by token budget
301
- let totalTokens = 0;
302
- const messagesWithinBudget = [];
303
- for (const msg of finalMessages) {
304
- const tokens = msg.metadata.tokensUsed || Math.ceil(msg.content.length / 4);
305
- if (totalTokens + tokens <= maxTokens) {
306
- messagesWithinBudget.push(msg);
307
- totalTokens += tokens;
308
- }
309
- else {
310
- break;
311
- }
312
- }
313
- // Get artifacts if requested (REAL VFS)
314
- let artifacts = [];
315
- if (includeArtifacts && this._vfs) {
316
- const artifactIds = new Set(messagesWithinBudget.flatMap(msg => msg.metadata.artifacts || []));
317
- for (const artifactId of artifactIds) {
318
- try {
319
- const entity = await this.brain.get(artifactId);
320
- if (entity) {
321
- artifacts.push({
322
- id: artifactId,
323
- path: entity.metadata?.path || artifactId,
324
- summary: entity.metadata?.description || undefined
325
- });
326
- }
327
- }
328
- catch (error) {
329
- // Artifact not found, skip
330
- continue;
331
- }
332
- }
333
- }
334
- // Get similar conversations if requested
335
- let similarConversations = [];
336
- if (includeSimilarConversations && conversationId && this.brain.neural) {
337
- // Use neural neighbors (REAL)
338
- try {
339
- const neighborsResult = await this.brain.neural().neighbors(conversationId, {
340
- limit: 5,
341
- minSimilarity: 0.7
342
- });
343
- similarConversations = neighborsResult.neighbors.map((neighbor) => ({
344
- id: neighbor.id,
345
- title: neighbor.metadata?.title,
346
- summary: neighbor.metadata?.summary,
347
- relevance: neighbor.score,
348
- messageCount: neighbor.metadata?.messageCount || 0
349
- }));
350
- }
351
- catch (error) {
352
- // Neighbors failed, skip
353
- console.warn('Similar conversation search failed:', error);
354
- }
355
- }
356
- const queryTime = Date.now() - startTime;
357
- return {
358
- messages: messagesWithinBudget.slice(0, limit),
359
- artifacts,
360
- similarConversations,
361
- totalTokens,
362
- metadata: {
363
- queryTime,
364
- messagesConsidered: results.length,
365
- conversationsSearched: new Set(results.map((r) => r.metadata.conversationId)).size
366
- }
367
- };
368
- }
369
- /**
370
- * Search messages semantically
371
- *
372
- * Uses: brain.find() with semantic search
373
- * Real implementation - vector similarity search
374
- *
375
- * @param options Search options
376
- * @returns Search results with scores
377
- */
378
- async searchMessages(options) {
379
- if (!this.initialized) {
380
- await this.init();
381
- }
382
- const { query, limit = 10, role, conversationId, sessionId, timeRange, includeMetadata = true, includeContent = true } = options;
383
- // Build filter
384
- const whereFilter = {};
385
- if (role) {
386
- whereFilter.role = Array.isArray(role) ? { $in: role } : role;
387
- }
388
- if (conversationId) {
389
- whereFilter.conversationId = conversationId;
390
- }
391
- if (sessionId) {
392
- whereFilter.sessionId = sessionId;
393
- }
394
- if (timeRange) {
395
- if (timeRange.start) {
396
- whereFilter.timestamp = { $gte: timeRange.start };
397
- }
398
- if (timeRange.end) {
399
- whereFilter.timestamp = { ...whereFilter.timestamp, $lte: timeRange.end };
400
- }
401
- }
402
- // Search with Triple Intelligence (REAL)
403
- const results = await this.brain.find({
404
- query: query,
405
- where: whereFilter,
406
- limit
407
- });
408
- // Format results
409
- return results.map((result) => {
410
- const message = {
411
- id: result.id,
412
- content: includeContent ? (result.data || result.content || '') : '',
413
- role: result.metadata.role,
414
- metadata: includeMetadata ? result.metadata : {},
415
- embedding: result.embedding,
416
- createdAt: result.metadata.timestamp || Date.now(),
417
- updatedAt: result.metadata.timestamp || Date.now()
418
- };
419
- // Create snippet
420
- const content = result.data || result.content || '';
421
- const snippet = content.length > 150 ? content.substring(0, 147) + '...' : content;
422
- return {
423
- message,
424
- score: result.score || 0,
425
- conversationId: result.metadata.conversationId,
426
- snippet: includeContent ? snippet : undefined
427
- };
428
- });
429
- }
430
- /**
431
- * Find similar conversations using Neural API
432
- *
433
- * Uses: brain.neural.neighbors()
434
- * Real implementation - semantic similarity with embeddings
435
- *
436
- * @param conversationId Conversation ID to find similar to
437
- * @param limit Maximum number of similar conversations
438
- * @param threshold Minimum similarity threshold
439
- * @returns Similar conversations with relevance scores
440
- */
441
- async findSimilarConversations(conversationId, limit = 5, threshold = 0.7) {
442
- if (!this.initialized) {
443
- await this.init();
444
- }
445
- if (!this.brain.neural) {
446
- throw new Error('Neural API not available');
447
- }
448
- // Use neural neighbors (REAL)
449
- const neighborsResult = await this.brain.neural().neighbors(conversationId, {
450
- limit: limit,
451
- minSimilarity: threshold
452
- });
453
- return neighborsResult.neighbors.map((neighbor) => ({
454
- id: neighbor.id,
455
- relevance: neighbor.score,
456
- metadata: neighbor.metadata
457
- }));
458
- }
459
- /**
460
- * Get conversation themes via clustering
461
- *
462
- * Uses: brain.neural.clusters()
463
- * Real implementation - semantic clustering
464
- *
465
- * @param conversationId Conversation ID
466
- * @returns Discovered themes
467
- */
468
- async getConversationThemes(conversationId) {
469
- if (!this.initialized) {
470
- await this.init();
471
- }
472
- if (!this.brain.neural) {
473
- throw new Error('Neural API not available');
474
- }
475
- // Get messages for conversation
476
- const results = await this.brain.find({
477
- where: { conversationId },
478
- limit: 1000
479
- });
480
- if (results.length === 0) {
481
- return [];
482
- }
483
- // Cluster messages (REAL)
484
- const clusters = await this.brain.neural().clusters({
485
- maxClusters: Math.min(5, Math.ceil(results.length / 5)),
486
- threshold: 0.75
487
- });
488
- // Convert to themes
489
- return clusters.map((cluster, index) => ({
490
- id: `theme_${index}`,
491
- label: cluster.label || `Theme ${index + 1}`,
492
- messages: cluster.members || [],
493
- centroid: cluster.centroid || [],
494
- coherence: cluster.coherence || 0
495
- }));
496
- }
497
- /**
498
- * Save an artifact (code, file, etc.) to VFS
499
- *
500
- * Uses: brain.vfs()
501
- * Real implementation - stores in virtual filesystem
502
- *
503
- * @param path VFS path
504
- * @param content File content
505
- * @param options Artifact options
506
- * @returns Artifact entity ID
507
- */
508
- async saveArtifact(path, content, options) {
509
- if (!this.initialized) {
510
- await this.init();
511
- }
512
- if (!this._vfs) {
513
- throw new Error('VFS not available');
514
- }
515
- // Write file to VFS (REAL)
516
- await this._vfs.writeFile(path, content);
517
- // Get the file entity
518
- const entity = await this._vfs.getEntity(path);
519
- // Link to conversation message if provided
520
- if (options.messageId) {
521
- await this.brain.relate({
522
- from: options.messageId,
523
- to: entity.id,
524
- type: VerbType.Creates,
525
- metadata: {
526
- conversationId: options.conversationId,
527
- artifactType: options.type || 'other'
528
- }
529
- });
530
- }
531
- return entity.id;
532
- }
533
- /**
534
- * Get conversation statistics
535
- *
536
- * Uses: brain.find() with aggregations
537
- * Real implementation - queries and aggregates data
538
- *
539
- * @param conversationId Optional conversation ID to filter
540
- * @returns Conversation statistics
541
- */
542
- async getConversationStats(conversationId) {
543
- if (!this.initialized) {
544
- await this.init();
545
- }
546
- // Query messages
547
- const whereFilter = conversationId ? { conversationId } : {};
548
- const results = await this.brain.find({
549
- where: whereFilter,
550
- limit: 100000 // Large limit for stats
551
- });
552
- // Calculate statistics (REAL aggregation)
553
- const conversations = new Set(results.map((r) => r.metadata.conversationId));
554
- const totalMessages = results.length;
555
- const totalTokens = results.reduce((sum, r) => sum + (r.metadata.tokensUsed || 0), 0);
556
- const timestamps = results.map((r) => r.metadata.timestamp || Date.now());
557
- const oldestMessage = Math.min(...timestamps);
558
- const newestMessage = Math.max(...timestamps);
559
- // Count by phase
560
- const phases = {};
561
- const roles = {};
562
- for (const result of results) {
563
- const phase = result.entity.metadata.problemSolvingPhase;
564
- const role = result.entity.metadata.role;
565
- if (phase) {
566
- phases[phase] = (phases[phase] || 0) + 1;
567
- }
568
- if (role) {
569
- roles[role] = (roles[role] || 0) + 1;
570
- }
571
- }
572
- return {
573
- totalConversations: conversations.size,
574
- totalMessages,
575
- totalTokens,
576
- averageMessagesPerConversation: totalMessages / Math.max(1, conversations.size),
577
- averageTokensPerMessage: totalTokens / Math.max(1, totalMessages),
578
- oldestMessage,
579
- newestMessage,
580
- phases: phases,
581
- roles: roles
582
- };
583
- }
584
- /**
585
- * Delete a message
586
- *
587
- * Uses: brain.deleteNoun()
588
- * Real implementation - removes from graph
589
- *
590
- * @param messageId Message ID to delete
591
- */
592
- async deleteMessage(messageId) {
593
- if (!this.initialized) {
594
- await this.init();
595
- }
596
- await this.brain.delete(messageId);
597
- }
598
- /**
599
- * Export conversation to JSON
600
- *
601
- * Uses: getConversationThread()
602
- * Real implementation - serializes conversation
603
- *
604
- * @param conversationId Conversation ID
605
- * @returns JSON-serializable conversation object
606
- */
607
- async exportConversation(conversationId) {
608
- if (!this.initialized) {
609
- await this.init();
610
- }
611
- const thread = await this.getConversationThread(conversationId, {
612
- includeArtifacts: true
613
- });
614
- return {
615
- version: '1.0',
616
- exportedAt: Date.now(),
617
- conversation: thread
618
- };
619
- }
620
- /**
621
- * Import conversation from JSON
622
- *
623
- * Uses: saveMessage() and linkMessages()
624
- * Real implementation - recreates conversation
625
- *
626
- * @param data Exported conversation data
627
- * @returns New conversation ID
628
- */
629
- async importConversation(data) {
630
- if (!this.initialized) {
631
- await this.init();
632
- }
633
- const newConversationId = `conv_${uuidv4()}`;
634
- const conversation = data.conversation;
635
- if (!conversation || !conversation.messages) {
636
- throw new Error('Invalid conversation data');
637
- }
638
- // Import messages in order
639
- const messageIdMap = new Map();
640
- for (let i = 0; i < conversation.messages.length; i++) {
641
- const msg = conversation.messages[i];
642
- const prevMessageId = i > 0 ? messageIdMap.get(conversation.messages[i - 1].id) : undefined;
643
- const newMessageId = await this.saveMessage(msg.content, msg.role, {
644
- conversationId: newConversationId,
645
- sessionId: conversation.metadata.sessionId,
646
- phase: msg.metadata.problemSolvingPhase,
647
- confidence: msg.metadata.confidence,
648
- tags: msg.metadata.tags,
649
- linkToPrevious: prevMessageId,
650
- metadata: msg.metadata
651
- });
652
- messageIdMap.set(msg.id, newMessageId);
653
- }
654
- return newConversationId;
655
- }
656
- }
657
- /**
658
- * Create a ConversationManager instance
659
- *
660
- * @param brain Brainy instance
661
- * @returns ConversationManager instance
662
- */
663
- export function createConversationManager(brain) {
664
- return new ConversationManager(brain);
665
- }
666
- //# sourceMappingURL=conversationManager.js.map
@@ -1,8 +0,0 @@
1
- /**
2
- * Conversation Module - Infinite Agent Memory
3
- *
4
- * Provides conversation and context management for AI agents
5
- * Built on Brainy's existing infrastructure
6
- */
7
- export { ConversationManager, createConversationManager } from './conversationManager.js';
8
- export type { MessageRole, ProblemSolvingPhase, ConversationMessage, ConversationMessageMetadata, ConversationThread, ConversationThreadMetadata, ConversationContext, RankedMessage, SaveMessageOptions, ContextRetrievalOptions, ConversationSearchOptions, ConversationSearchResult, ConversationTheme, ArtifactOptions, ConversationStats, CompactionOptions, CompactionResult } from './types.js';
@@ -1,8 +0,0 @@
1
- /**
2
- * Conversation Module - Infinite Agent Memory
3
- *
4
- * Provides conversation and context management for AI agents
5
- * Built on Brainy's existing infrastructure
6
- */
7
- export { ConversationManager, createConversationManager } from './conversationManager.js';
8
- //# sourceMappingURL=index.js.map