UNPKG

harmonycode

Version:

The AI collaboration framework that prevents echo chambers - Real-time collaboration with diversity enforcement

451 lines 15.5 kB
"use strict"; /** * HarmonyCode v3.2.0 - Identity Manager * Implements persistent agent identity separate from roles and sessions * Enhanced with unique name enforcement and session cleanup (v3.2) */ 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.IdentityManager = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const crypto = __importStar(require("crypto")); class IdentityManager { constructor(workspacePath = '.harmonycode') { this.identities = new Map(); this.tokenToAgent = new Map(); this.sessionToAgent = new Map(); this.nameToAgent = new Map(); // New: displayName -> agentId mapping this.persistPath = path.join(workspacePath, 'identities.json'); this.loadIdentities(); } /** * Register a new agent or retrieve existing one */ registerAgent(displayName, role = 'general') { // This method should only be called for new agents in v3.2 // Use getOrCreateAgent for the new behavior // For backward compatibility, still create new agent // but log a warning console.warn(`Warning: Creating new agent with name '${displayName}'. Multiple agents with same name detected.`); // Create new agent identity const agentId = this.generateAgentId(); const authToken = this.generateAuthToken(); const identity = { agentId, displayName, firstSeen: new Date(), lastSeen: new Date(), currentRole: role, roleHistory: [{ role, timestamp: new Date(), sessionId: 'initial' }], perspectiveHistory: [], stats: { totalSessions: 0, totalMessages: 0, totalTasks: 0, totalEdits: 0, diversityScore: 0.5, agreementRate: 0.5, evidenceRate: 0.5 }, authToken }; this.identities.set(agentId, identity); this.tokenToAgent.set(authToken, agentId); this.nameToAgent.set(displayName, agentId); // Track name -> agentId mapping this.saveIdentities(); return identity; } /** * Authenticate an agent using their token */ authenticateAgent(authToken) { const agentId = this.tokenToAgent.get(authToken); if (!agentId) return null; const identity = this.identities.get(agentId); if (!identity) return null; // Update last seen identity.lastSeen = new Date(); this.saveIdentities(); return identity; } /** * Connect an agent to a session */ connectAgentToSession(agentId, sessionId) { const identity = this.identities.get(agentId); if (!identity) return; // Disconnect from previous session if any if (identity.currentSessionId) { this.sessionToAgent.delete(identity.currentSessionId); } // Connect to new session identity.currentSessionId = sessionId; identity.lastActivityTime = new Date(); // v3.2: Track activity identity.stats.totalSessions++; this.sessionToAgent.set(sessionId, agentId); this.saveIdentities(); } /** * Disconnect an agent from their session */ disconnectAgent(sessionId) { const agentId = this.sessionToAgent.get(sessionId); if (!agentId) return; const identity = this.identities.get(agentId); if (identity) { identity.currentSessionId = undefined; this.saveIdentities(); } this.sessionToAgent.delete(sessionId); } /** * Change an agent's role */ changeAgentRole(agentId, newRole, sessionId) { const identity = this.identities.get(agentId); if (!identity) return; // Record role transition identity.roleHistory.push({ role: identity.currentRole, timestamp: new Date(), sessionId }); identity.currentRole = newRole; this.saveIdentities(); } /** * Change an agent's perspective (for diversity) */ changeAgentPerspective(agentId, newPerspective, reason) { const identity = this.identities.get(agentId); if (!identity) return; // Record perspective transition if (identity.currentPerspective) { identity.perspectiveHistory.push({ perspective: identity.currentPerspective, timestamp: new Date(), reason }); } identity.currentPerspective = newPerspective; this.saveIdentities(); } /** * Get agent by session ID */ getAgentBySessionId(sessionId) { const agentId = this.sessionToAgent.get(sessionId); if (!agentId) return null; return this.identities.get(agentId) || null; } /** * Get agent by agent ID */ getAgentById(agentId) { return this.identities.get(agentId) || null; } /** * Update agent statistics */ updateAgentStats(agentId, updates) { const identity = this.identities.get(agentId); if (!identity) return; identity.stats = { ...identity.stats, ...updates }; this.saveIdentities(); } /** * Get all active agents */ getActiveAgents() { return Array.from(this.identities.values()) .filter(agent => agent.currentSessionId !== undefined); } /** * Get agents by role */ getAgentsByRole(role) { return Array.from(this.identities.values()) .filter(agent => agent.currentRole === role); } /** * Get agent history report */ getAgentHistoryReport(agentId) { const identity = this.identities.get(agentId); if (!identity) return 'Agent not found'; const report = [ `Agent: ${identity.displayName} (${identity.agentId})`, `First seen: ${identity.firstSeen.toISOString()}`, `Last seen: ${identity.lastSeen.toISOString()}`, `Current role: ${identity.currentRole}`, `Total sessions: ${identity.stats.totalSessions}`, ``, `Role History:`, ...identity.roleHistory.map(r => ` - ${r.role} at ${r.timestamp.toISOString()}`), ``, `Diversity Metrics:`, ` - Diversity score: ${identity.stats.diversityScore}`, ` - Agreement rate: ${identity.stats.agreementRate}`, ` - Evidence rate: ${identity.stats.evidenceRate}` ]; return report.join('\n'); } /** * Find agent by display name (v3.2) */ findAgentByDisplayName(displayName) { const agentId = this.nameToAgent.get(displayName); if (!agentId) return null; return this.identities.get(agentId) || null; } /** * Check if a display name is available (v3.2) */ isNameAvailable(displayName) { return !this.nameToAgent.has(displayName); } /** * Get or create agent with unique name enforcement (v3.2) */ getOrCreateAgent(displayName, role = 'general', authToken) { // If auth token provided, try to authenticate first if (authToken) { const authenticated = this.authenticateAgent(authToken); if (authenticated) { return authenticated; } } // Check if name already exists const existing = this.findAgentByDisplayName(displayName); if (existing) { // Update last seen and return existing agent existing.lastSeen = new Date(); this.saveIdentities(); return existing; } // Create new agent with unique name return this.createNewAgent(displayName, role); } /** * Create new agent with unique name (v3.2) */ createNewAgent(displayName, role) { const agentId = this.generateAgentId(); const authToken = this.generateAuthToken(); const identity = { agentId, displayName, firstSeen: new Date(), lastSeen: new Date(), currentRole: role, roleHistory: [{ role, timestamp: new Date(), sessionId: 'initial' }], perspectiveHistory: [], stats: { totalSessions: 0, totalMessages: 0, totalTasks: 0, totalEdits: 0, diversityScore: 0.5, agreementRate: 0.5, evidenceRate: 0.5 }, authToken }; this.identities.set(agentId, identity); this.tokenToAgent.set(authToken, agentId); this.nameToAgent.set(displayName, agentId); this.saveIdentities(); return identity; } /** * Get name suggestions when a name is taken (v3.2) */ getNameSuggestions(baseName, count = 3) { const suggestions = []; let counter = 2; // Try numbered suffixes while (suggestions.length < count && counter <= 10) { const suggestion = `${baseName}${counter}`; if (this.isNameAvailable(suggestion)) { suggestions.push(suggestion); } counter++; } // Try with underscores if (suggestions.length < count) { const suggestion = `${baseName}_new`; if (this.isNameAvailable(suggestion)) { suggestions.push(suggestion); } } // Try with role suffix if (suggestions.length < count) { const suggestion = `${baseName}_agent`; if (this.isNameAvailable(suggestion)) { suggestions.push(suggestion); } } return suggestions.slice(0, count); } /** * Update agent activity time (v3.2) */ updateAgentActivity(agentId) { const identity = this.identities.get(agentId); if (identity && identity.currentSessionId) { identity.lastActivityTime = new Date(); this.saveIdentities(); } } /** * Clean up inactive sessions (v3.2) */ cleanupInactiveSessions(timeoutMs = 300000) { let cleanedCount = 0; const cutoffTime = new Date(Date.now() - timeoutMs); for (const [agentId, identity] of this.identities) { if (identity.currentSessionId && identity.lastActivityTime && identity.lastActivityTime < cutoffTime) { console.log(`🧹 Cleaning up inactive session for ${identity.displayName} (${agentId})`); // Disconnect from session this.sessionToAgent.delete(identity.currentSessionId); identity.currentSessionId = undefined; identity.lastActivityTime = undefined; cleanedCount++; } } if (cleanedCount > 0) { this.saveIdentities(); console.log(`✅ Cleaned up ${cleanedCount} inactive sessions`); } return cleanedCount; } /** * Get session activity report (v3.2) */ getSessionActivityReport() { const activeAgents = this.getActiveAgents(); const totalAgents = this.identities.size; return { active: activeAgents.length, inactive: totalAgents - activeAgents.length, total: totalAgents }; } /** * Generate unique agent ID */ generateAgentId() { return `agent-${crypto.randomBytes(8).toString('hex')}`; } /** * Generate authentication token */ generateAuthToken() { return crypto.randomBytes(32).toString('hex'); } /** * Load identities from disk */ loadIdentities() { try { if (fs.existsSync(this.persistPath)) { const data = fs.readFileSync(this.persistPath, 'utf-8'); const parsed = JSON.parse(data); // Reconstruct maps parsed.identities.forEach((identity) => { // Convert date strings back to Date objects identity.firstSeen = new Date(identity.firstSeen); identity.lastSeen = new Date(identity.lastSeen); identity.roleHistory.forEach((r) => { r.timestamp = new Date(r.timestamp); }); identity.perspectiveHistory.forEach((p) => { p.timestamp = new Date(p.timestamp); }); this.identities.set(identity.agentId, identity); this.tokenToAgent.set(identity.authToken, identity.agentId); this.nameToAgent.set(identity.displayName, identity.agentId); // v3.2: Track name mapping if (identity.currentSessionId) { this.sessionToAgent.set(identity.currentSessionId, identity.agentId); } }); } } catch (error) { console.error('Failed to load identities:', error); } } /** * Save identities to disk */ saveIdentities() { try { const data = { identities: Array.from(this.identities.values()), version: '3.2.0' }; fs.writeFileSync(this.persistPath, JSON.stringify(data, null, 2)); } catch (error) { console.error('Failed to save identities:', error); } } } exports.IdentityManager = IdentityManager; //# sourceMappingURL=identity-manager.js.map