globals.ts•6.94 kB
// Global state management for Agent-MCP Node.js
// Ported from Python core/globals.py
/**
* Centralized mutable global state for the MCP server
* This replaces the Python globals module with TypeScript equivalents
*/
// Agent data interface
interface AgentData {
agent_id: string;
capabilities: string[];
status: 'created' | 'active' | 'terminated' | 'failed';
current_task?: string;
working_directory: string;
color: string;
created_at: string;
updated_at: string;
}
// File lock info interface
interface FileLockInfo {
agent_id: string;
timestamp: string;
status: 'locked' | 'unlocked';
operation?: string;
}
// Task data interface (for in-memory cache)
interface TaskData {
task_id: string;
title: string;
description: string;
assigned_to?: string;
created_by: string;
status: string;
priority: string;
created_at: string;
updated_at: string;
parent_task?: string;
child_tasks: string[];
depends_on_tasks: string[];
notes: any[];
}
// Audit log entry interface
interface AuditLogEntry {
timestamp: string;
agent_id: string;
action: string;
details: any;
}
/**
* Global state container
* Maintains all server-wide state that needs to persist across requests
*/
export const globalState = {
// --- Core Server State ---
/**
* Client connections tracking
* Maps client ID to connection data
*/
connections: new Map<string, any>(),
/**
* Active agents registry
* Maps agent token to agent data
*/
activeAgents: new Map<string, AgentData>(),
/**
* Runtime admin token
* Generated during server startup or loaded from environment
*/
adminToken: null as string | null,
/**
* Tasks in-memory cache
* Maps task ID to task data for quick access
*/
tasks: new Map<string, TaskData>(),
// --- File and Directory State ---
/**
* File locking state
* Maps file path to lock information
*/
fileMap: new Map<string, FileLockInfo>(),
/**
* Agent working directories
* Maps agent ID to absolute working directory path
*/
agentWorkingDirs: new Map<string, string>(),
// --- Tmux Session Management ---
/**
* Active tmux sessions
* Maps agent ID to tmux session name
*/
agentTmuxSessions: new Map<string, string>(),
// --- Auditing and Agent Management ---
/**
* In-memory audit log for current session
* Persistent log should be stored in files/database
*/
auditLog: [] as AuditLogEntry[],
/**
* Agent profile counter for cycling Cursor profiles
*/
agentProfileCounter: 20,
/**
* Agent color index for cycling through available colors
*/
agentColorIndex: 0,
// --- Server Lifecycle ---
/**
* Server running flag
* Controls main server loop and background tasks
*/
serverRunning: true,
/**
* Server start time for uptime calculations
*/
serverStartTime: new Date().toISOString(),
// --- External Service Clients ---
/**
* OpenAI client instance placeholder
* Will be initialized by external service modules
*/
openaiClientInstance: null as any,
// --- Database/VSS State ---
/**
* VSS load test status
* Tracks if sqlite-vec extension loadability has been tested
*/
vssLoadTested: false,
/**
* VSS load success status
* Indicates if sqlite-vec extension was successfully loaded
*/
vssLoadSuccessful: false,
// --- Background Task Handles ---
/**
* RAG indexing background task handle
* Used for cancellation and lifecycle management
*/
ragIndexTaskHandle: null as any,
/**
* Claude Code session monitoring task handle
*/
claudeSessionTaskHandle: null as any,
// --- Statistics and Monitoring ---
/**
* Request statistics
*/
stats: {
totalRequests: 0,
totalToolCalls: 0,
totalErrors: 0,
startTime: Date.now()
}
};
/**
* Helper functions for global state management
*/
/**
* Reset global state (useful for testing)
*/
export function resetGlobalState(): void {
globalState.connections.clear();
globalState.activeAgents.clear();
globalState.tasks.clear();
globalState.fileMap.clear();
globalState.agentWorkingDirs.clear();
globalState.agentTmuxSessions.clear();
globalState.auditLog.length = 0;
globalState.agentProfileCounter = 20;
globalState.agentColorIndex = 0;
globalState.serverRunning = true;
globalState.stats.totalRequests = 0;
globalState.stats.totalToolCalls = 0;
globalState.stats.totalErrors = 0;
globalState.stats.startTime = Date.now();
}
/**
* Get server statistics
*/
export function getServerStats() {
const uptime = Date.now() - globalState.stats.startTime;
return {
uptime,
uptimeHours: Math.floor(uptime / (1000 * 60 * 60)),
uptimeMinutes: Math.floor((uptime % (1000 * 60 * 60)) / (1000 * 60)),
totalRequests: globalState.stats.totalRequests,
totalToolCalls: globalState.stats.totalToolCalls,
totalErrors: globalState.stats.totalErrors,
activeAgents: globalState.activeAgents.size,
activeTasks: globalState.tasks.size,
lockedFiles: globalState.fileMap.size,
tmuxSessions: globalState.agentTmuxSessions.size,
auditEntries: globalState.auditLog.length
};
}
/**
* Increment request counter
*/
export function incrementRequestCount(): void {
globalState.stats.totalRequests++;
}
/**
* Increment tool call counter
*/
export function incrementToolCallCount(): void {
globalState.stats.totalToolCalls++;
}
/**
* Increment error counter
*/
export function incrementErrorCount(): void {
globalState.stats.totalErrors++;
}
/**
* Add entry to audit log
*/
export function addAuditLogEntry(agentId: string, action: string, details: any = {}): void {
globalState.auditLog.push({
timestamp: new Date().toISOString(),
agent_id: agentId,
action,
details
});
// Keep audit log size reasonable (last 1000 entries)
if (globalState.auditLog.length > 1000) {
globalState.auditLog.splice(0, globalState.auditLog.length - 1000);
}
}
/**
* Get recent audit log entries
*/
export function getAuditLog(limit: number = 50, agentId?: string, action?: string): AuditLogEntry[] {
let filtered = globalState.auditLog;
if (agentId) {
filtered = filtered.filter(entry => entry.agent_id === agentId);
}
if (action) {
filtered = filtered.filter(entry => entry.action === action);
}
return filtered.slice(-limit);
}
/**
* Update task cache
*/
export function updateTaskCache(taskId: string, taskData: TaskData): void {
globalState.tasks.set(taskId, taskData);
}
/**
* Remove task from cache
*/
export function removeTaskFromCache(taskId: string): void {
globalState.tasks.delete(taskId);
}
/**
* Get task from cache
*/
export function getTaskFromCache(taskId: string): TaskData | undefined {
return globalState.tasks.get(taskId);
}
console.log('✅ Global state management loaded');