harmonycode
Version:
The AI collaboration framework that prevents echo chambers - Real-time collaboration with diversity enforcement
958 lines • 39.4 kB
JavaScript
"use strict";
/**
* HarmonyCode v3.2.0 - Core WebSocket Server
* Real-time collaboration with anti-echo-chamber enforcement
* Enhanced with unique name enforcement and session cleanup
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.HarmonyCodeServer = void 0;
const ws_1 = require("ws");
const events_1 = require("events");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const middleware_1 = require("../diversity/middleware");
const engine_1 = require("../orchestration/engine");
const session_manager_enhanced_1 = require("./session-manager-enhanced");
const identity_manager_1 = require("./identity-manager");
const message_router_1 = require("./message-router");
const realtime_enhancer_1 = require("./realtime-enhancer");
class HarmonyCodeServer extends events_1.EventEmitter {
constructor(config = { port: 8765, enableAntiEcho: true }) {
super();
this.config = config;
this.projectPath = process.cwd();
this.identityManager = new identity_manager_1.IdentityManager(path.join(this.projectPath, '.harmonycode'));
this.sessions = new session_manager_enhanced_1.EnhancedSessionManager(this.identityManager);
this.router = new message_router_1.MessageRouter();
this.diversity = new middleware_1.DiversityMiddleware(config.diversityConfig);
this.orchestration = new engine_1.OrchestrationEngine(config.orchestrationConfig);
this.realtimeEnhancer = new realtime_enhancer_1.RealtimeEnhancer({
watchPaths: [path.join(this.projectPath, '.harmonycode')],
enableNotifications: true,
enableLiveCursors: true
});
// Connect components
this.diversity.on('intervention', this.handleDiversityIntervention.bind(this));
this.orchestration.on('taskCreated', this.broadcastTask.bind(this));
}
/**
* Start the HarmonyCode server
*/
async start() {
this.wss = new ws_1.WebSocketServer({ port: this.config.port });
console.log(`
╔════════════════════════════════════════════════════════╗
║ 🎵 HarmonyCode v3.2.0 Server 🎵 ║
║ ║
║ Real-time collaboration with persistent identity ║
║ Anti-echo-chamber: ${this.config.enableAntiEcho ? 'ENABLED ✓' : 'DISABLED'} ║
╚════════════════════════════════════════════════════════╝
`);
console.log(`🚀 Server running on ws://localhost:${this.config.port}`);
console.log(`📂 Project directory: ${this.projectPath}\n`);
// Initialize project structure
this.initializeProjectStructure();
// Set up WebSocket handlers
this.wss.on('connection', this.handleConnection.bind(this));
// Start orchestration engine
await this.orchestration.initialize();
// Start real-time file watching
this.realtimeEnhancer.startWatching();
this.setupRealtimeHandlers();
// Monitor diversity metrics
if (this.config.enableAntiEcho) {
this.startDiversityMonitoring();
}
// Start session cleanup (v3.2)
this.startSessionCleanup();
}
/**
* Check version compatibility between client and server (v3.2)
*/
checkVersionCompatibility(clientVersion) {
const serverVersion = '3.2.0';
if (!clientVersion) {
return {
message: 'Client version unknown - please upgrade your CLI',
severity: 'warning',
upgradeAction: 'npm install -g harmonycode@latest'
};
}
if (clientVersion === serverVersion) {
return null; // Perfect match
}
const parseVersion = (version) => {
const parts = version.replace(/[^0-9.]/g, '').split('.');
return {
major: parseInt(parts[0] || '0'),
minor: parseInt(parts[1] || '0'),
patch: parseInt(parts[2] || '0')
};
};
const client = parseVersion(clientVersion);
const server = parseVersion(serverVersion);
// Major version difference (breaking changes)
if (client.major !== server.major) {
return {
message: `Major version mismatch! Client: ${clientVersion}, Server: ${serverVersion}`,
severity: 'error',
upgradeAction: client.major < server.major
? `npm install -g harmonycode@${serverVersion}`
: 'Server needs upgrading'
};
}
// Minor version difference (new features)
if (client.minor !== server.minor) {
const isClientOlder = client.minor < server.minor ||
(client.minor === server.minor && client.patch < server.patch);
return {
message: isClientOlder
? `Client outdated: ${clientVersion} < ${serverVersion}. Missing v3.2 features!`
: `Client newer: ${clientVersion} > ${serverVersion}. Some features may not work.`,
severity: 'warning',
upgradeAction: isClientOlder
? `npm install -g harmonycode@${serverVersion}`
: 'Consider upgrading server'
};
}
// Patch version difference (bug fixes)
if (client.patch !== server.patch) {
const isClientOlder = client.patch < server.patch;
return {
message: isClientOlder
? `Client has older patch: ${clientVersion} (latest: ${serverVersion})`
: `Client has newer patch: ${clientVersion} (server: ${serverVersion})`,
severity: 'warning',
upgradeAction: isClientOlder ? `npm install -g harmonycode@${serverVersion}` : undefined
};
}
return null;
}
/**
* Handle new WebSocket connection with identity support
*/
handleConnection(ws, req) {
const sessionId = this.generateSessionId();
// Don't create session yet - wait for authentication
ws.on('message', async (data) => {
try {
const message = JSON.parse(data.toString());
if (message.type === 'auth') {
await this.handleAuthentication(ws, sessionId, message);
}
else if (message.type === 'register') {
await this.handleRegistration(ws, message);
}
else {
// Reject non-auth messages from unauthenticated connections
ws.send(JSON.stringify({
type: 'error',
message: 'Authentication required. Send auth message first.'
}));
}
}
catch (error) {
ws.send(JSON.stringify({
type: 'error',
message: 'Invalid message format'
}));
}
});
ws.on('error', (error) => console.error('WebSocket error:', error));
}
/**
* Handle agent authentication
*/
async handleAuthentication(ws, sessionId, authMessage) {
try {
const { agentName, authToken, role = 'general', perspective, clientVersion } = authMessage;
// Version compatibility check (v3.2)
const versionWarning = this.checkVersionCompatibility(clientVersion);
if (versionWarning) {
console.log(`⚠️ Version mismatch for ${agentName}: ${versionWarning.message}`);
}
// Create or authenticate session
const session = this.sessions.createSession(sessionId, ws, authToken, agentName, role);
const agentIdentity = session.agentIdentity;
const isReturning = agentIdentity.stats.totalSessions > 1;
console.log(`✅ ${agentIdentity.displayName} connected (${session.currentRole})`);
console.log(` Agent ID: ${agentIdentity.agentId}`);
if (isReturning) {
console.log(` Welcome back! Sessions: ${agentIdentity.stats.totalSessions}`);
}
// Send auth success with identity info
ws.send(JSON.stringify({
type: 'auth-success',
agentId: agentIdentity.agentId,
authToken: agentIdentity.authToken,
isReturning,
totalSessions: agentIdentity.stats.totalSessions,
totalContributions: agentIdentity.stats.totalMessages + agentIdentity.stats.totalTasks + agentIdentity.stats.totalEdits,
lastSeen: agentIdentity.lastSeen,
serverVersion: '3.2.0',
clientVersion: clientVersion || 'unknown',
versionWarning: versionWarning || null, // v3.2: Include version compatibility warning
capabilities: {
realtime: true,
orchestration: true,
antiEchoChamber: this.config.enableAntiEcho,
persistentIdentity: true,
sparcModes: this.orchestration.getAvailableModes()
}
}));
// Assign perspective if anti-echo enabled
if (this.config.enableAntiEcho && perspective) {
this.sessions.changeSessionPerspective(sessionId, perspective);
console.log(` Assigned perspective: ${perspective}`);
}
else if (this.config.enableAntiEcho) {
const assignedPerspective = this.diversity.assignPerspective(sessionId);
this.sessions.changeSessionPerspective(sessionId, assignedPerspective);
console.log(` Assigned perspective: ${assignedPerspective}`);
}
// Set up authenticated message handlers
ws.removeAllListeners('message');
ws.on('message', (data) => this.handleMessage(session, data));
ws.on('close', () => this.handleDisconnect(session));
// Set up real-time update stream for this session
this.realtimeEnhancer.createUpdateStream(ws);
// Notify other sessions
this.broadcastSessionUpdate('joined', session);
}
catch (error) {
console.error('Authentication error:', error);
ws.send(JSON.stringify({
type: 'auth-failed',
reason: error.message || 'Authentication failed'
}));
ws.close();
}
}
/**
* Handle agent registration (v3.2)
*/
async handleRegistration(ws, registerMessage) {
try {
const { agentName, role = 'general', forceNew = false } = registerMessage;
if (!agentName) {
ws.send(JSON.stringify({
type: 'register-failed',
reason: 'Agent name is required'
}));
ws.close();
return;
}
// Check if name is available
if (!forceNew && !this.identityManager.isNameAvailable(agentName)) {
const suggestions = this.identityManager.getNameSuggestions(agentName, 3);
ws.send(JSON.stringify({
type: 'register-failed',
reason: 'name-taken',
suggestions
}));
ws.close();
return;
}
// Create new agent identity
const identity = forceNew
? this.identityManager.registerAgent(agentName, role) // Allow duplicate if forced
: this.identityManager.createNewAgent(agentName, role);
console.log(`✅ New agent registered: ${agentName} (${identity.agentId})`);
// Send success response
ws.send(JSON.stringify({
type: 'register-success',
agentId: identity.agentId,
agentName: identity.displayName,
authToken: identity.authToken,
role: identity.currentRole
}));
ws.close();
}
catch (error) {
console.error('Registration error:', error);
ws.send(JSON.stringify({
type: 'register-failed',
reason: error.message || 'Registration failed'
}));
ws.close();
}
}
/**
* Handle incoming message with diversity checks
*/
async handleMessage(session, data) {
try {
const message = JSON.parse(data.toString());
// Log message for analysis
console.log(`💬 ${session.name}: ${message.type}`);
// Apply diversity middleware if enabled
if (this.config.enableAntiEcho && this.shouldCheckDiversity(message.type)) {
const diversityCheck = await this.diversity.checkMessage({
sessionId: session.id,
content: message.content || message.text,
type: message.type,
evidence: message.evidence
});
if (!diversityCheck.allowed) {
// Send intervention requirement
session.ws.send(JSON.stringify({
type: 'diversity-intervention',
reason: diversityCheck.reason,
requiredAction: diversityCheck.requiredAction,
suggestions: diversityCheck.suggestions
}));
console.log(`❌ Diversity check failed: ${diversityCheck.reason}`);
return;
}
}
// Route message through orchestration if needed
if (this.shouldOrchestrate(message.type)) {
await this.orchestration.processMessage(session.id, message);
}
// Handle different message types
switch (message.type) {
case 'edit':
await this.handleEdit(session, message);
this.sessions.incrementSessionMetric(session.id, 'edits');
break;
case 'task':
await this.handleTask(session, message);
if (message.action === 'create' || message.action === 'complete') {
this.sessions.incrementSessionMetric(session.id, 'tasks');
}
break;
case 'vote':
await this.handleVote(session, message);
break;
case 'message':
await this.handleChatMessage(session, message);
this.sessions.incrementSessionMetric(session.id, 'messages');
break;
case 'spawn':
await this.handleSpawnRequest(session, message);
break;
case 'whoami':
await this.handleWhoAmI(session);
break;
case 'switch-role':
await this.handleRoleSwitch(session, message);
break;
case 'get-history':
await this.handleGetHistory(session);
break;
default:
// Let router handle custom messages
await this.router.route(session, message);
}
// Record in diversity tracker
if (this.config.enableAntiEcho) {
this.diversity.recordContribution(session.id, message);
}
// Update activity time for session cleanup (v3.2)
this.identityManager.updateAgentActivity(session.agentId);
}
catch (error) {
console.error(`Error handling message from ${session.name}:`, error);
session.ws.send(JSON.stringify({
type: 'error',
message: 'Failed to process message'
}));
}
}
/**
* Handle collaborative editing with conflict resolution
*/
async handleEdit(session, message) {
const { file, edit, version } = message;
// Apply edit through conflict resolution
const result = await this.orchestration.applyEdit({
file,
edit,
version,
sessionId: session.id
});
if (result.conflict) {
// Use diversity-weighted resolution
const resolution = await this.diversity.resolveConflict(result.conflicts || []);
// Broadcast resolved edit
this.broadcast({
type: 'edit-resolved',
file,
edit: resolution.edit,
resolvedBy: 'diversity-weighted-consensus',
confidence: resolution.confidence
});
}
else {
// Broadcast successful edit
this.broadcast({
type: 'edit',
file,
edit,
sessionId: session.id,
timestamp: Date.now()
}, session.id);
}
}
/**
* Handle task creation and assignment
*/
async handleTask(session, message) {
const { action, task } = message;
switch (action) {
case 'create':
// Create task with diversity requirements
const enhancedTask = {
...task,
requiresPerspectives: this.diversity.getRequiredPerspectives(task.type),
evidenceRequired: task.type === 'decision' || task.type === 'analysis'
};
const created = await this.orchestration.createTask(enhancedTask);
this.broadcastTask('created', created);
break;
case 'claim':
// Check if agent perspective matches task needs
const canClaim = await this.diversity.canClaimTask(session.id, task.id);
if (canClaim) {
await this.orchestration.assignTask(task.id, session.id);
this.broadcastTask('assigned', { taskId: task.id, agentId: session.id });
}
else {
session.ws.send(JSON.stringify({
type: 'task-rejection',
reason: 'Perspective mismatch - different viewpoint needed'
}));
}
break;
}
}
/**
* Handle voting with diversity weighting
*/
async handleVote(session, message) {
const { proposalId, vote, evidence } = message;
// Record vote with perspective weight
const weight = this.diversity.calculateVoteWeight(session.id, vote, evidence);
await this.orchestration.recordVote({
proposalId,
sessionId: session.id,
vote,
weight,
evidence,
perspective: session.perspective
});
// Check if voting complete
const result = await this.orchestration.checkVotingComplete(proposalId);
if (result.complete) {
// Resolve with diversity-weighted consensus
const decision = await this.diversity.resolveDecision(result.votes || []);
this.broadcast({
type: 'decision-made',
proposalId,
decision: decision.choice,
confidence: decision.confidence,
diversityScore: decision.diversityScore,
perspectives: decision.perspectivesRepresented
});
}
}
/**
* Handle SPARC mode spawn requests
*/
async handleSpawnRequest(session, message) {
const { mode, task, count = 1 } = message;
// Spawn agents with diverse perspectives
const agents = await this.orchestration.spawnAgents({
mode, // researcher, coder, analyst, etc.
task,
count,
ensureDiversity: this.config.enableAntiEcho
});
// Assign complementary perspectives
if (this.config.enableAntiEcho) {
agents.forEach(agent => {
const perspective = this.diversity.assignComplementaryPerspective(this.sessions.getActivePerspectives());
agent.perspective = perspective;
});
}
session.ws.send(JSON.stringify({
type: 'agents-spawned',
agents: agents.map(a => ({
id: a.id,
mode: a.mode,
perspective: a.perspective,
capabilities: a.capabilities
}))
}));
}
/**
* Handle diversity intervention events
*/
handleDiversityIntervention(intervention) {
const session = this.sessions.getSession(intervention.targetAgent);
if (session) {
session.ws.send(JSON.stringify({
type: 'intervention-required',
interventionType: intervention.type,
reason: intervention.reason,
requiredAction: intervention.requiredAction,
deadline: intervention.deadline
}));
// Log intervention
console.log(`🚨 Diversity intervention for ${session.agentIdentity.displayName}: ${intervention.reason}`);
}
}
/**
* Monitor and report diversity metrics
*/
startDiversityMonitoring() {
setInterval(() => {
const metrics = this.diversity.getMetrics();
// Broadcast metrics to all sessions
this.broadcast({
type: 'diversity-metrics',
metrics: {
overallDiversity: metrics.overallDiversity,
agreementRate: metrics.agreementRate,
evidenceRate: metrics.evidenceRate,
perspectiveDistribution: metrics.perspectiveDistribution,
recentInterventions: metrics.interventions
}
});
// Log warnings if needed
if (metrics.agreementRate > 0.8) {
console.log('⚠️ High agreement rate detected - echo chamber risk!');
}
if (metrics.overallDiversity < 0.5) {
console.log('⚠️ Low diversity score - consider perspective rotation');
}
}, 30000); // Every 30 seconds
}
/**
* Start session cleanup for ghost sessions (v3.2)
*/
startSessionCleanup() {
this.cleanupInterval = setInterval(() => {
const cleanedCount = this.identityManager.cleanupInactiveSessions(300000); // 5 minutes
if (cleanedCount > 0) {
// Broadcast session update to remaining clients
this.broadcast({
type: 'session-cleanup',
cleanedSessions: cleanedCount,
timestamp: Date.now()
});
}
// Log session activity report every hour
const now = new Date();
if (now.getMinutes() === 0) {
const report = this.identityManager.getSessionActivityReport();
console.log(`📊 Session Activity: ${report.active} active, ${report.inactive} inactive, ${report.total} total`);
}
}, 60000); // Every minute
}
/**
* Initialize project directory structure
*/
initializeProjectStructure() {
const dirs = [
'.harmonycode',
'.harmonycode/tasks',
'.harmonycode/messages',
'.harmonycode/memory',
'.harmonycode/decisions'
];
dirs.forEach(dir => {
const fullPath = path.join(this.projectPath, dir);
if (!fs.existsSync(fullPath)) {
fs.mkdirSync(fullPath, { recursive: true });
}
});
// Create discussion board if doesn't exist
const boardPath = path.join(this.projectPath, '.harmonycode', 'DISCUSSION_BOARD.md');
if (!fs.existsSync(boardPath)) {
fs.writeFileSync(boardPath, '# Discussion Board\n\nAI agents discuss here with diversity enforcement.\n\n');
}
}
/**
* Set up real-time event handlers
*/
setupRealtimeHandlers() {
// Handle task board updates
this.realtimeEnhancer.on('task-board-updated', (event) => {
console.log('📋 Task board updated - notifying all sessions');
this.broadcast({
type: 'realtime-update',
updateType: 'task-board',
timestamp: event.timestamp
});
});
// Handle discussion updates
this.realtimeEnhancer.on('discussion-updated', (event) => {
console.log('💬 Discussion board updated - notifying all sessions');
this.broadcast({
type: 'realtime-update',
updateType: 'discussion',
timestamp: event.timestamp
});
});
// Handle new messages
this.realtimeEnhancer.on('new-message', (event) => {
console.log('📨 New message detected');
this.broadcast({
type: 'realtime-update',
updateType: 'new-message',
filename: event.filename,
timestamp: event.timestamp
});
});
// Handle concurrent editing notifications
this.realtimeEnhancer.on('concurrent-editing', (data) => {
console.log(`⚠️ Multiple editors on ${data.filepath}`);
data.editors.forEach((editorId) => {
const session = this.sessions.getSession(editorId);
if (session) {
session.ws.send(JSON.stringify({
type: 'concurrent-editing-warning',
filepath: data.filepath,
otherEditors: data.editors.filter((id) => id !== editorId)
}));
}
});
});
}
/**
* Broadcast message to all or specific sessions
*/
broadcast(message, excludeSessionId) {
const data = JSON.stringify(message);
this.sessions.getAllSessions().forEach(session => {
if (session.id !== excludeSessionId && session.ws.readyState === ws_1.WebSocket.OPEN) {
session.ws.send(data);
}
});
}
/**
* Broadcast task updates
*/
broadcastTask(event, task) {
this.broadcast({
type: 'task-update',
event,
task
});
}
/**
* Broadcast session updates
*/
broadcastSessionUpdate(event, session) {
this.broadcast({
type: 'session-update',
event,
session: {
id: session.id,
agentId: session.agentId,
displayName: session.agentIdentity.displayName,
role: session.currentRole,
perspective: session.currentPerspective
}
}, session.id);
}
/**
* Handle chat messages
*/
async handleChatMessage(session, message) {
// Add to discussion board with identity info
const boardPath = path.join(this.projectPath, '.harmonycode', 'DISCUSSION_BOARD.md');
const identity = session.agentIdentity;
const entry = `\n## ${identity.displayName} (${session.currentRole})\n**Agent ID**: ${identity.agentId}\n**Perspective**: ${session.currentPerspective || 'None'}\n**Time**: ${new Date().toISOString()}\n\n${message.text}\n\n---\n`;
fs.appendFileSync(boardPath, entry);
// Broadcast to others
this.broadcast({
type: 'chat',
sessionId: session.id,
agentId: session.agentId,
displayName: identity.displayName,
role: session.currentRole,
perspective: session.currentPerspective,
text: message.text,
timestamp: Date.now()
}, session.id);
}
/**
* Handle session disconnect
*/
handleDisconnect(session) {
console.log(`👋 ${session.name} disconnected`);
this.sessions.removeSession(session.id);
this.diversity.removeAgent(session.id);
this.orchestration.handleAgentDisconnect(session.id);
this.broadcastSessionUpdate('left', session);
}
/**
* Handle WebSocket errors
*/
handleError(session, error) {
console.error(`Error in session ${session.name}:`, error);
}
/**
* Check if message type should be diversity-checked
*/
shouldCheckDiversity(messageType) {
const checkedTypes = ['edit', 'vote', 'proposal', 'decision', 'message'];
return checkedTypes.includes(messageType);
}
/**
* Check if message should be orchestrated
*/
shouldOrchestrate(messageType) {
const orchestratedTypes = ['task', 'spawn', 'swarm', 'workflow'];
return orchestratedTypes.includes(messageType);
}
/**
* Handle identity query with enhanced identity card (v3.2)
*/
async handleWhoAmI(session) {
const identity = session.agentIdentity;
const activeSessions = this.sessions.getAllSessions().length;
const agentRank = this.calculateAgentRank(identity);
const timeInSystem = Date.now() - identity.firstSeen.getTime();
const daysSinceJoined = Math.floor(timeInSystem / (1000 * 60 * 60 * 24));
session.ws.send(JSON.stringify({
type: 'identity-card',
card: {
// Basic Identity
agentId: identity.agentId,
displayName: identity.displayName,
firstSeen: identity.firstSeen,
lastSeen: identity.lastSeen,
daysSinceJoined,
// Current Status
currentRole: session.currentRole,
currentPerspective: session.currentPerspective,
isActive: true,
// Statistics & Rankings
stats: identity.stats,
rank: agentRank,
// Session Information
sessionInfo: {
sessionId: session.id,
joinedAt: session.joinedAt,
currentSessionContributions: {
messages: session.sessionMessages || 0,
edits: session.sessionEdits || 0,
tasks: session.sessionTasks || 0
}
},
// History & Evolution
roleHistory: identity.roleHistory.slice(-5), // Last 5 role changes
perspectiveHistory: identity.perspectiveHistory.slice(-3), // Last 3 perspective changes
// System Context
systemInfo: {
totalActiveSessions: activeSessions,
serverVersion: '3.2.0',
antiEchoChamberEnabled: this.config.enableAntiEcho,
diversityScore: identity.stats.diversityScore,
evidenceRate: identity.stats.evidenceRate
},
// Achievement Badges
badges: this.calculateAchievementBadges(identity),
// Recommendations
recommendations: this.generateRecommendations(identity, session)
}
}));
}
/**
* Calculate agent rank based on contributions (v3.2)
*/
calculateAgentRank(identity) {
const totalContributions = identity.stats.totalMessages + identity.stats.totalTasks + identity.stats.totalEdits;
const diversityBonus = identity.stats.diversityScore * 10;
const evidenceBonus = identity.stats.evidenceRate * 5;
const score = totalContributions + diversityBonus + evidenceBonus;
if (score >= 100)
return { title: 'Master Collaborator', level: 5, nextLevel: 'Legend' };
if (score >= 50)
return { title: 'Senior Contributor', level: 4, nextLevel: 'Master Collaborator' };
if (score >= 25)
return { title: 'Active Member', level: 3, nextLevel: 'Senior Contributor' };
if (score >= 10)
return { title: 'Contributor', level: 2, nextLevel: 'Active Member' };
return { title: 'Newcomer', level: 1, nextLevel: 'Contributor' };
}
/**
* Calculate achievement badges (v3.2)
*/
calculateAchievementBadges(identity) {
const badges = [];
if (identity.stats.totalSessions >= 10)
badges.push('🏆 Veteran');
if (identity.stats.diversityScore >= 0.8)
badges.push('🌈 Diversity Champion');
if (identity.stats.evidenceRate >= 0.8)
badges.push('📊 Evidence Expert');
if (identity.stats.totalMessages >= 50)
badges.push('💬 Communicator');
if (identity.stats.totalTasks >= 20)
badges.push('📋 Task Master');
if (identity.stats.totalEdits >= 30)
badges.push('✏️ Editor');
if (identity.roleHistory.length >= 5)
badges.push('🎭 Role Explorer');
const daysSinceJoined = Math.floor((Date.now() - identity.firstSeen.getTime()) / (1000 * 60 * 60 * 24));
if (daysSinceJoined >= 30)
badges.push('📅 Long-term Collaborator');
if (daysSinceJoined >= 7)
badges.push('📈 Consistent Contributor');
return badges;
}
/**
* Generate personalized recommendations (v3.2)
*/
generateRecommendations(identity, session) {
const recommendations = [];
if (identity.stats.diversityScore < 0.5) {
recommendations.push('Try adopting different perspectives to increase diversity');
}
if (identity.stats.evidenceRate < 0.6) {
recommendations.push('Include more evidence in your arguments and proposals');
}
if (identity.roleHistory.length < 3) {
recommendations.push('Experiment with different roles to broaden your experience');
}
if (identity.stats.totalTasks < 5) {
recommendations.push('Consider creating or claiming tasks to increase collaboration');
}
const recentMessages = identity.stats.totalMessages / Math.max(identity.stats.totalSessions, 1);
if (recentMessages < 2) {
recommendations.push('Share more insights in discussions to help the team');
}
return recommendations;
}
/**
* Handle role switch request
*/
async handleRoleSwitch(session, message) {
const { newRole } = message;
if (!newRole) {
session.ws.send(JSON.stringify({
type: 'error',
message: 'New role required'
}));
return;
}
this.sessions.changeSessionRole(session.id, newRole);
session.ws.send(JSON.stringify({
type: 'role-changed',
oldRole: session.currentRole,
newRole: newRole,
agentId: session.agentId
}));
console.log(`🔄 ${session.agentIdentity.displayName} switched role from ${session.currentRole} to ${newRole}`);
// Notify others
this.broadcastSessionUpdate('role-changed', session);
}
/**
* Handle history request
*/
async handleGetHistory(session) {
const report = this.sessions.getAgentSessionHistory(session.agentId);
session.ws.send(JSON.stringify({
type: 'history-report',
report
}));
}
/**
* Generate unique session ID
*/
generateSessionId() {
return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
/**
* Extract session ID from WebSocket URL (deprecated)
*/
extractSessionId(url) {
const match = url?.match(/\/(.+)$/);
return match ? match[1] : this.generateSessionId();
}
/**
* Graceful shutdown
*/
async stop() {
console.log('\n👋 Shutting down HarmonyCode server...');
// Save state
await this.orchestration.saveState();
await this.diversity.saveMetrics();
// Stop real-time watchers
this.realtimeEnhancer.destroy();
// Stop session cleanup timer (v3.2)
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
}
// Close connections
this.sessions.getAllSessions().forEach(session => {
session.ws.close();
});
this.wss.close();
console.log('✅ Server stopped');
}
}
exports.HarmonyCodeServer = HarmonyCodeServer;
// Export for use as module
exports.default = HarmonyCodeServer;
// Run if called directly
if (require.main === module) {
const server = new HarmonyCodeServer({
port: parseInt(process.env.HARMONYCODE_PORT || '8765'),
enableAntiEcho: process.env.DISABLE_ANTI_ECHO !== 'true',
diversityConfig: {
minimumDiversity: 0.6,
disagreementQuota: 0.3,
evidenceThreshold: 0.5
},
orchestrationConfig: {
enableSPARC: true,
swarmMode: 'distributed',
maxAgents: 10
}
});
server.start();
// Handle graceful shutdown
process.on('SIGINT', async () => {
await server.stop();
process.exit(0);
});
}
//# sourceMappingURL=server.js.map