#!/usr/bin/env node /** * BZZZ SDK JavaScript Collaborative Agent Example * ============================================== * * Demonstrates building a collaborative agent using BZZZ SDK for Node.js. * Shows real-time coordination, decision sharing, and event-driven workflows. */ const { BzzzClient, EventType, DecisionType } = require('bzzz-sdk'); const EventEmitter = require('events'); class CollaborativeAgent extends EventEmitter { constructor(config) { super(); this.config = { endpoint: 'http://localhost:8080', role: 'frontend_developer', agentId: 'collaborative-agent-js', ...config }; this.client = null; this.isRunning = false; this.stats = { eventsProcessed: 0, decisionsPublished: 0, collaborationsStarted: 0, tasksCompleted: 0 }; this.collaborationQueue = []; this.activeCollaborations = new Map(); } async initialize() { console.log('๐Ÿš€ Initializing BZZZ Collaborative Agent'); try { // Create BZZZ client this.client = new BzzzClient({ endpoint: this.config.endpoint, role: this.config.role, agentId: this.config.agentId, timeout: 30000, retryCount: 3 }); // Test connection const status = await this.client.getStatus(); console.log(`โœ… Connected as ${status.agentId} (${status.role})`); console.log(` Node ID: ${status.nodeId}`); console.log(` Authority: ${status.authorityLevel}`); console.log(` Can decrypt: ${status.canDecrypt.join(', ')}`); return true; } catch (error) { console.error('โŒ Failed to initialize BZZZ client:', error.message); return false; } } async start() { console.log('๐ŸŽฏ Starting collaborative agent...'); this.isRunning = true; // Set up event listeners await this.setupEventListeners(); // Start background tasks this.startBackgroundTasks(); // Announce availability await this.announceAvailability(); console.log('โœ… Collaborative agent is running'); console.log(' Use Ctrl+C to stop'); } async setupEventListeners() { console.log('๐ŸŽง Setting up event listeners...'); try { // System events const eventStream = this.client.subscribeEvents(); eventStream.on('event', (event) => this.handleSystemEvent(event)); eventStream.on('error', (error) => console.error('Event stream error:', error)); // Decision stream for collaboration opportunities const decisionStream = this.client.decisions.streamDecisions({ contentType: 'decision', // Listen to all roles for collaboration opportunities }); decisionStream.on('decision', (decision) => this.handleDecision(decision)); decisionStream.on('error', (error) => console.error('Decision stream error:', error)); console.log('โœ… Event listeners configured'); } catch (error) { console.error('โŒ Failed to setup event listeners:', error.message); } } startBackgroundTasks() { // Process collaboration queue setInterval(() => this.processCollaborationQueue(), 5000); // Publish status updates setInterval(() => this.publishStatusUpdate(), 30000); // Clean up old collaborations setInterval(() => this.cleanupCollaborations(), 60000); // Simulate autonomous work setInterval(() => this.simulateAutonomousWork(), 45000); } async handleSystemEvent(event) { this.stats.eventsProcessed++; switch (event.type) { case EventType.DECISION_PUBLISHED: await this.handleDecisionPublished(event); break; case EventType.PEER_CONNECTED: await this.handlePeerConnected(event); break; case EventType.ADMIN_CHANGED: console.log(`๐Ÿ‘‘ Admin changed: ${event.data.oldAdmin} โ†’ ${event.data.newAdmin}`); break; default: console.log(`๐Ÿ“ก System event: ${event.type}`); } } async handleDecisionPublished(event) { const { address, creatorRole, contentType } = event.data; // Check if this decision needs collaboration if (await this.needsCollaboration(event.data)) { console.log(`๐Ÿค Collaboration opportunity: ${address}`); this.collaborationQueue.push({ address, creatorRole, contentType, timestamp: new Date(), priority: this.calculatePriority(event.data) }); } } async handlePeerConnected(event) { const { agentId, role } = event.data; console.log(`๐ŸŒ New peer connected: ${agentId} (${role})`); // Check if this peer can help with pending collaborations await this.checkCollaborationOpportunities(role); } async handleDecision(decision) { console.log(`๐Ÿ“‹ Decision received: ${decision.task} from ${decision.role}`); // Analyze decision for collaboration potential if (this.canContribute(decision)) { await this.offerCollaboration(decision); } } async needsCollaboration(eventData) { // Simple heuristic: collaboration needed for architectural decisions // or when content mentions frontend/UI concerns return eventData.contentType === 'architectural' || (eventData.summary && eventData.summary.toLowerCase().includes('frontend')) || (eventData.summary && eventData.summary.toLowerCase().includes('ui')); } calculatePriority(eventData) { let priority = 1; if (eventData.contentType === 'architectural') priority += 2; if (eventData.creatorRole === 'senior_software_architect') priority += 1; if (eventData.summary && eventData.summary.includes('urgent')) priority += 3; return Math.min(priority, 5); // Cap at 5 } canContribute(decision) { const frontendKeywords = ['react', 'vue', 'angular', 'frontend', 'ui', 'css', 'javascript']; const content = decision.decision.toLowerCase(); return frontendKeywords.some(keyword => content.includes(keyword)); } async processCollaborationQueue() { if (this.collaborationQueue.length === 0) return; // Sort by priority and age this.collaborationQueue.sort((a, b) => { const priorityDiff = b.priority - a.priority; if (priorityDiff !== 0) return priorityDiff; return a.timestamp - b.timestamp; // Earlier timestamp = higher priority }); // Process top collaboration const collaboration = this.collaborationQueue.shift(); await this.startCollaboration(collaboration); } async startCollaboration(collaboration) { console.log(`๐Ÿค Starting collaboration: ${collaboration.address}`); this.stats.collaborationsStarted++; try { // Get the original decision content const content = await this.client.decisions.getContent(collaboration.address); // Analyze and provide frontend perspective const frontendAnalysis = await this.analyzeFrontendImpact(content); // Publish collaborative response await this.client.decisions.publishArchitectural({ task: `frontend_analysis_${collaboration.address.split('/').pop()}`, decision: `Frontend impact analysis for: ${content.task}`, rationale: frontendAnalysis.rationale, alternatives: frontendAnalysis.alternatives, implications: frontendAnalysis.implications, nextSteps: frontendAnalysis.nextSteps }); console.log(`โœ… Published frontend analysis for ${collaboration.address}`); this.stats.decisionsPublished++; // Track active collaboration this.activeCollaborations.set(collaboration.address, { startTime: new Date(), status: 'active', contributions: 1 }); } catch (error) { console.error(`โŒ Failed to start collaboration: ${error.message}`); } } async analyzeFrontendImpact(content) { // Simulate frontend analysis based on the content const analysis = { rationale: "Frontend perspective analysis", alternatives: [], implications: [], nextSteps: [] }; const contentLower = content.decision.toLowerCase(); if (contentLower.includes('api') || contentLower.includes('service')) { analysis.rationale = "API changes will require frontend integration updates"; analysis.implications.push("Frontend API client needs updating"); analysis.implications.push("UI loading states may need adjustment"); analysis.nextSteps.push("Update API client interfaces"); analysis.nextSteps.push("Test error handling in UI"); } if (contentLower.includes('database') || contentLower.includes('schema')) { analysis.implications.push("Data models in frontend may need updates"); analysis.nextSteps.push("Review frontend data validation"); analysis.nextSteps.push("Update TypeScript interfaces if applicable"); } if (contentLower.includes('security') || contentLower.includes('auth')) { analysis.implications.push("Authentication flow in UI requires review"); analysis.nextSteps.push("Update login/logout components"); analysis.nextSteps.push("Review JWT handling in frontend"); } // Add some alternatives analysis.alternatives.push("Progressive rollout with feature flags"); analysis.alternatives.push("A/B testing for UI changes"); return analysis; } async offerCollaboration(decision) { console.log(`๐Ÿ’ก Offering collaboration on: ${decision.task}`); // Create a collaboration offer await this.client.decisions.publishCode({ task: `collaboration_offer_${Date.now()}`, decision: `Frontend developer available for collaboration on: ${decision.task}`, filesModified: [], // No files yet linesChanged: 0, testResults: { passed: 0, failed: 0, coverage: 0 }, language: 'javascript' }); } async checkCollaborationOpportunities(peerRole) { // If a senior architect joins, they might want to collaborate if (peerRole === 'senior_software_architect' && this.collaborationQueue.length > 0) { console.log(`๐ŸŽฏ Senior architect available - prioritizing collaborations`); // Boost priority of architectural collaborations this.collaborationQueue.forEach(collab => { if (collab.contentType === 'architectural') { collab.priority = Math.min(collab.priority + 1, 5); } }); } } async simulateAutonomousWork() { if (!this.isRunning) return; console.log('๐Ÿ”„ Performing autonomous frontend work...'); const tasks = [ 'optimize_bundle_size', 'update_component_library', 'improve_accessibility', 'refactor_styling', 'add_responsive_design' ]; const randomTask = tasks[Math.floor(Math.random() * tasks.length)]; try { await this.client.decisions.publishCode({ task: randomTask, decision: `Autonomous frontend improvement: ${randomTask.replace(/_/g, ' ')}`, filesModified: [ `src/components/${randomTask}.js`, `src/styles/${randomTask}.css`, `tests/${randomTask}.test.js` ], linesChanged: Math.floor(Math.random() * 100) + 20, testResults: { passed: Math.floor(Math.random() * 10) + 5, failed: Math.random() < 0.1 ? 1 : 0, coverage: Math.random() * 20 + 80 }, language: 'javascript' }); this.stats.tasksCompleted++; console.log(`โœ… Completed autonomous task: ${randomTask}`); } catch (error) { console.error(`โŒ Failed autonomous task: ${error.message}`); } } async publishStatusUpdate() { if (!this.isRunning) return; try { await this.client.decisions.publishSystemStatus({ status: "Collaborative agent operational", metrics: { eventsProcessed: this.stats.eventsProcessed, decisionsPublished: this.stats.decisionsPublished, collaborationsStarted: this.stats.collaborationsStarted, tasksCompleted: this.stats.tasksCompleted, activeCollaborations: this.activeCollaborations.size, queueLength: this.collaborationQueue.length }, healthChecks: { client_connected: !!this.client, event_streaming: this.isRunning, collaboration_system: this.collaborationQueue.length < 10 } }); } catch (error) { console.error(`โŒ Failed to publish status: ${error.message}`); } } async announceAvailability() { try { await this.client.decisions.publishArchitectural({ task: 'agent_availability', decision: 'Collaborative frontend agent is now available', rationale: 'Providing frontend expertise and collaboration capabilities', implications: [ 'Can analyze frontend impact of backend changes', 'Available for UI/UX collaboration', 'Monitors for frontend-related decisions' ], nextSteps: [ 'Listening for collaboration opportunities', 'Ready to provide frontend perspective', 'Autonomous frontend improvement tasks active' ] }); console.log('๐Ÿ“ข Announced availability to BZZZ network'); } catch (error) { console.error(`โŒ Failed to announce availability: ${error.message}`); } } async cleanupCollaborations() { const now = new Date(); const oneHour = 60 * 60 * 1000; for (const [address, collaboration] of this.activeCollaborations) { if (now - collaboration.startTime > oneHour) { console.log(`๐Ÿงน Cleaning up old collaboration: ${address}`); this.activeCollaborations.delete(address); } } // Also clean up old queue items this.collaborationQueue = this.collaborationQueue.filter( collab => now - collab.timestamp < oneHour ); } printStats() { console.log('\n๐Ÿ“Š Agent Statistics:'); console.log(` Events processed: ${this.stats.eventsProcessed}`); console.log(` Decisions published: ${this.stats.decisionsPublished}`); console.log(` Collaborations started: ${this.stats.collaborationsStarted}`); console.log(` Tasks completed: ${this.stats.tasksCompleted}`); console.log(` Active collaborations: ${this.activeCollaborations.size}`); console.log(` Queue length: ${this.collaborationQueue.length}`); } async stop() { console.log('\n๐Ÿ›‘ Stopping collaborative agent...'); this.isRunning = false; try { // Publish shutdown notice await this.client.decisions.publishSystemStatus({ status: "Collaborative agent shutting down", metrics: this.stats, healthChecks: { client_connected: false, event_streaming: false, collaboration_system: false } }); // Close client connection if (this.client) { await this.client.close(); } this.printStats(); console.log('โœ… Collaborative agent stopped gracefully'); } catch (error) { console.error(`โŒ Error during shutdown: ${error.message}`); } } } // Main execution async function main() { const agent = new CollaborativeAgent({ role: 'frontend_developer', agentId: 'collaborative-frontend-js' }); // Handle graceful shutdown process.on('SIGINT', async () => { console.log('\n๐Ÿ”„ Received shutdown signal...'); await agent.stop(); process.exit(0); }); try { // Initialize and start the agent if (await agent.initialize()) { await agent.start(); // Keep running until stopped process.on('SIGTERM', () => { agent.stop().then(() => process.exit(0)); }); } else { console.error('โŒ Failed to initialize collaborative agent'); process.exit(1); } } catch (error) { console.error('โŒ Unexpected error:', error.message); process.exit(1); } } // Export for use as module module.exports = CollaborativeAgent; // Run if called directly if (require.main === module) { main().catch(error => { console.error('โŒ Fatal error:', error); process.exit(1); }); }