/** * BrainyMCPClient * * Client for connecting Claude instances to the Brain Jar Broadcast Server * Utilizes Brainy for persistent memory and vector search capabilities */ import WebSocket from 'ws'; import { BrainyData } from '../brainyData.js'; import { v4 as uuidv4 } from '../universal/uuid.js'; export class BrainyMCPClient { constructor(options) { this.messageHandlers = new Map(); this.isConnected = false; this.options = { serverUrl: 'ws://localhost:8765', autoReconnect: true, useBrainyMemory: true, ...options }; } /** * Initialize Brainy for persistent memory */ async initBrainy() { if (this.options.useBrainyMemory && !this.brainy) { this.brainy = new BrainyData({ storage: { requestPersistentStorage: true } }); await this.brainy.init(); console.log(`🧠 Brainy memory initialized for ${this.options.name}`); } } /** * Connect to the broadcast server */ async connect() { // Initialize Brainy first await this.initBrainy(); return new Promise((resolve, reject) => { try { this.socket = new WebSocket(this.options.serverUrl); this.socket.on('open', () => { console.log(`✅ ${this.options.name} connected to Brain Jar Broadcast`); this.isConnected = true; // Identify ourselves this.send({ type: 'identify', data: { name: this.options.name, role: this.options.role } }); resolve(); }); this.socket.on('message', async (data) => { try { const message = JSON.parse(data.toString()); await this.handleMessage(message); } catch (error) { console.error('Error parsing message:', error); } }); this.socket.on('close', () => { console.log(`❌ ${this.options.name} disconnected from Brain Jar`); this.isConnected = false; if (this.options.autoReconnect) { this.scheduleReconnect(); } }); this.socket.on('error', (error) => { console.error(`Connection error for ${this.options.name}:`, error); reject(error); }); } catch (error) { reject(error); } }); } /** * Handle incoming message */ async handleMessage(message) { // Store in Brainy for persistent memory if (this.brainy && message.type === 'message') { try { await this.brainy.add({ text: `${message.from}: ${JSON.stringify(message.data)}`, metadata: { messageId: message.id, from: message.from, to: message.to, timestamp: message.timestamp, type: message.type, event: message.event } }); } catch (error) { console.error('Error storing message in Brainy:', error); } } // Handle sync messages (receive history) if (message.type === 'sync' && message.data.history) { console.log(`📜 ${this.options.name} received ${message.data.history.length} historical messages`); // Store history in Brainy if (this.brainy) { for (const histMsg of message.data.history) { await this.brainy.add({ text: `${histMsg.from}: ${JSON.stringify(histMsg.data)}`, metadata: histMsg }); } } } // Call registered handlers const handler = this.messageHandlers.get(message.type); if (handler) { handler(message); } // Call universal handler const universalHandler = this.messageHandlers.get('*'); if (universalHandler) { universalHandler(message); } } /** * Send a message */ send(message) { if (!this.socket || this.socket.readyState !== WebSocket.OPEN) { console.error(`${this.options.name} is not connected`); return; } const fullMessage = { id: message.id || uuidv4(), from: this.options.name, type: message.type || 'message', data: message.data || {}, timestamp: Date.now(), ...message }; this.socket.send(JSON.stringify(fullMessage)); } /** * Send a message to specific agent(s) */ sendTo(recipient, data) { this.send({ to: recipient, type: 'message', data }); } /** * Broadcast to all agents */ broadcast(data) { this.send({ type: 'message', data }); } /** * Register a message handler */ on(type, handler) { this.messageHandlers.set(type, handler); } /** * Remove a message handler */ off(type) { this.messageHandlers.delete(type); } /** * Search historical messages using Brainy's vector search */ async searchMemory(query, limit = 10) { if (!this.brainy) { console.warn('Brainy memory not initialized'); return []; } const results = await this.brainy.search(query, limit); return results.map(r => ({ ...r.metadata, relevance: r.score })); } /** * Get recent messages from Brainy memory */ async getRecentMessages(limit = 20) { if (!this.brainy) { console.warn('Brainy memory not initialized'); return []; } // Search for recent activity const results = await this.brainy.search('recent messages communication', limit); return results .map(r => r.metadata) .sort((a, b) => b.timestamp - a.timestamp); } /** * Schedule reconnection attempt */ scheduleReconnect() { if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); } this.reconnectTimeout = setTimeout(() => { console.log(`🔄 ${this.options.name} attempting to reconnect...`); this.connect().catch(error => { console.error('Reconnection failed:', error); this.scheduleReconnect(); }); }, 5000); } /** * Disconnect from server */ disconnect() { if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); } if (this.socket) { this.socket.close(1000, 'Client disconnecting'); this.socket = undefined; } this.isConnected = false; } /** * Check if connected */ getIsConnected() { return this.isConnected; } /** * Get agent info */ getAgentInfo() { return { name: this.options.name, role: this.options.role, connected: this.isConnected }; } } // Export for both environments export default BrainyMCPClient; //# sourceMappingURL=brainyMCPClient.js.map