snow-flow
Version:
Snow-Flow v3.2.0: Complete ServiceNow Enterprise Suite with 180+ MCP Tools. ATF Testing, Knowledge Management, Service Catalog, Change Management with CAB scheduling, Virtual Agent chatbots with NLU, Performance Analytics KPIs, Flow Designer automation, A
423 lines (416 loc) • 16.7 kB
JavaScript
"use strict";
/**
* MCP Memory Manager
* Shared memory integration for all MCP servers to coordinate with agents
*/
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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MCPMemoryManager = void 0;
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const logger_js_1 = require("../../utils/logger.js");
class MCPMemoryManager {
constructor() {
this.logger = new logger_js_1.Logger('MCPMemoryManager');
// Create memory directory if it doesn't exist
const memoryDir = path.join(process.cwd(), '.snow-flow', 'memory');
if (!fs.existsSync(memoryDir)) {
fs.mkdirSync(memoryDir, { recursive: true });
}
// Initialize database
this.db = new better_sqlite3_1.default(path.join(memoryDir, 'mcp-coordination.db'));
this.initializeDatabase();
}
static getInstance() {
if (!MCPMemoryManager.instance) {
MCPMemoryManager.instance = new MCPMemoryManager();
}
return MCPMemoryManager.instance;
}
initializeDatabase() {
this.logger.info('Initializing MCP coordination database');
this.db.exec(`
-- Agent coordination and communication
CREATE TABLE IF NOT EXISTS agent_coordination (
session_id TEXT NOT NULL,
agent_id TEXT NOT NULL,
agent_type TEXT NOT NULL,
status TEXT NOT NULL,
assigned_tasks TEXT,
progress_percentage INTEGER DEFAULT 0,
last_activity TEXT NOT NULL,
current_tool TEXT,
error_state TEXT,
PRIMARY KEY (session_id, agent_id)
);
-- ServiceNow artifact tracking
CREATE TABLE IF NOT EXISTS servicenow_artifacts (
sys_id TEXT PRIMARY KEY,
artifact_type TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT,
created_by_agent TEXT NOT NULL,
session_id TEXT NOT NULL,
deployment_status TEXT NOT NULL,
update_set_id TEXT,
dependencies TEXT,
metadata TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- Inter-agent communication
CREATE TABLE IF NOT EXISTS agent_messages (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
from_agent TEXT NOT NULL,
to_agent TEXT NOT NULL,
message_type TEXT NOT NULL,
content TEXT NOT NULL,
artifact_reference TEXT,
timestamp TEXT NOT NULL,
processed INTEGER DEFAULT 0
);
-- Shared context between agents
CREATE TABLE IF NOT EXISTS shared_context (
session_id TEXT NOT NULL,
context_key TEXT NOT NULL,
context_value TEXT NOT NULL,
created_by_agent TEXT NOT NULL,
expires_at TEXT,
access_permissions TEXT,
created_at TEXT DEFAULT (datetime('now')),
PRIMARY KEY (session_id, context_key)
);
-- Deployment tracking
CREATE TABLE IF NOT EXISTS deployment_history (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
artifact_sys_id TEXT NOT NULL,
deployment_type TEXT NOT NULL,
success INTEGER NOT NULL,
deployment_time TEXT NOT NULL,
agent_id TEXT NOT NULL,
error_details TEXT,
rollback_available INTEGER DEFAULT 0
);
-- Agent dependencies and handoffs
CREATE TABLE IF NOT EXISTS agent_dependencies (
session_id TEXT NOT NULL,
agent_id TEXT NOT NULL,
depends_on_agent TEXT NOT NULL,
dependency_type TEXT NOT NULL,
artifact_reference TEXT,
status TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now')),
satisfied_at TEXT,
PRIMARY KEY (session_id, agent_id, depends_on_agent)
);
-- Performance metrics
CREATE TABLE IF NOT EXISTS performance_metrics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
agent_id TEXT NOT NULL,
operation_name TEXT NOT NULL,
duration_ms INTEGER NOT NULL,
success INTEGER NOT NULL,
error_message TEXT,
timestamp TEXT NOT NULL
);
-- Create indexes for better performance
CREATE INDEX IF NOT EXISTS idx_artifacts_session ON servicenow_artifacts(session_id);
CREATE INDEX IF NOT EXISTS idx_coordination_session ON agent_coordination(session_id);
CREATE INDEX IF NOT EXISTS idx_messages_session ON agent_messages(session_id);
CREATE INDEX IF NOT EXISTS idx_context_session ON shared_context(session_id);
`);
}
// Read session context
async getSessionContext(session_id) {
try {
const contexts = this.db.prepare(`
SELECT * FROM shared_context
WHERE session_id = ?
AND (expires_at IS NULL OR expires_at > datetime('now'))
`).all(session_id);
const result = {};
for (const context of contexts) {
try {
result[context.context_key] = JSON.parse(context.context_value);
}
catch {
result[context.context_key] = context.context_value;
}
}
return result;
}
catch (error) {
this.logger.error('Failed to get session context', error);
return {};
}
}
// Get active agents for session
async getActiveAgents(session_id) {
try {
const agents = this.db.prepare(`
SELECT * FROM agent_coordination
WHERE session_id = ? AND status IN ('active', 'spawned')
`).all(session_id);
return agents;
}
catch (error) {
this.logger.error('Failed to get active agents', error);
return [];
}
}
// Store artifact information
async storeArtifact(artifact) {
try {
const stmt = this.db.prepare(`
INSERT OR REPLACE INTO servicenow_artifacts
(sys_id, artifact_type, name, description, created_by_agent, session_id,
deployment_status, update_set_id, dependencies, metadata)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
stmt.run(artifact.sys_id, artifact.artifact_type, artifact.name, artifact.description || null, artifact.created_by_agent, artifact.session_id, artifact.deployment_status, artifact.update_set_id || null, artifact.dependencies || null, artifact.metadata || null);
this.logger.debug(`Stored artifact ${artifact.name} (${artifact.sys_id})`);
}
catch (error) {
this.logger.error('Failed to store artifact', error);
throw error;
}
}
// Update shared context
async updateSharedContext(context) {
try {
const stmt = this.db.prepare(`
INSERT OR REPLACE INTO shared_context
(session_id, context_key, context_value, created_by_agent, expires_at, access_permissions)
VALUES (?, ?, ?, ?, ?, ?)
`);
stmt.run(context.session_id, context.context_key, context.context_value, context.created_by_agent, context.expires_at ? context.expires_at.toISOString() : null, context.access_permissions || null);
this.logger.debug(`Updated shared context: ${context.context_key}`);
}
catch (error) {
this.logger.error('Failed to update shared context', error);
throw error;
}
}
// Update agent coordination status
async updateAgentCoordination(coordination) {
try {
// Build dynamic update query based on provided fields
const updates = [];
const values = [];
if (coordination.status !== undefined) {
updates.push('status = ?');
values.push(coordination.status);
}
if (coordination.progress_percentage !== undefined) {
updates.push('progress_percentage = ?');
values.push(coordination.progress_percentage);
}
if (coordination.current_tool !== undefined) {
updates.push('current_tool = ?');
values.push(coordination.current_tool);
}
if (coordination.error_state !== undefined) {
updates.push('error_state = ?');
values.push(coordination.error_state);
}
// Always update last_activity
updates.push('last_activity = datetime("now")');
// Add WHERE clause values
values.push(coordination.agent_id, coordination.session_id);
const stmt = this.db.prepare(`
UPDATE agent_coordination
SET ${updates.join(', ')}
WHERE agent_id = ? AND session_id = ?
`);
const result = stmt.run(...values);
// If no rows updated, create new record
if (result.changes === 0) {
const insertStmt = this.db.prepare(`
INSERT INTO agent_coordination
(session_id, agent_id, agent_type, status, progress_percentage, last_activity)
VALUES (?, ?, ?, ?, ?, datetime('now'))
`);
insertStmt.run(coordination.session_id, coordination.agent_id, coordination.agent_type || 'unknown', coordination.status || 'active', coordination.progress_percentage || 0);
}
this.logger.debug(`Updated agent coordination for ${coordination.agent_id}`);
}
catch (error) {
this.logger.error('Failed to update agent coordination', error);
throw error;
}
}
// Send message between agents
async sendAgentMessage(message) {
try {
const id = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const stmt = this.db.prepare(`
INSERT INTO agent_messages
(id, session_id, from_agent, to_agent, message_type, content, artifact_reference, timestamp, processed)
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'), 0)
`);
stmt.run(id, message.session_id, message.from_agent, message.to_agent, message.message_type, message.content, message.artifact_reference || null);
this.logger.debug(`Sent message from ${message.from_agent} to ${message.to_agent}`);
}
catch (error) {
this.logger.error('Failed to send agent message', error);
throw error;
}
}
// Check for pending messages
async checkForMessages(agent_id, session_id) {
try {
const messages = this.db.prepare(`
SELECT * FROM agent_messages
WHERE session_id = ? AND to_agent = ? AND processed = 0
ORDER BY timestamp ASC
`).all(session_id, agent_id);
// Mark messages as processed
if (messages.length > 0) {
const ids = messages.map((m) => m.id);
const placeholders = ids.map(() => '?').join(',');
this.db.prepare(`
UPDATE agent_messages
SET processed = 1
WHERE id IN (${placeholders})
`).run(...ids);
}
return messages;
}
catch (error) {
this.logger.error('Failed to check for messages', error);
return [];
}
}
// Track performance metrics
async trackPerformance(metric) {
try {
const stmt = this.db.prepare(`
INSERT INTO performance_metrics
(session_id, agent_id, operation_name, duration_ms, success, error_message, timestamp)
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
`);
stmt.run(metric.session_id, metric.agent_id, metric.operation_name, metric.duration_ms, metric.success ? 1 : 0, metric.error_message || null);
this.logger.debug(`Tracked performance: ${metric.operation_name} (${metric.duration_ms}ms)`);
}
catch (error) {
this.logger.error('Failed to track performance', error);
}
}
// Record deployment
async recordDeployment(session_id, artifact_sys_id, deployment_type, success, agent_id, error_details) {
try {
const id = `deploy_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const stmt = this.db.prepare(`
INSERT INTO deployment_history
(id, session_id, artifact_sys_id, deployment_type, success, deployment_time, agent_id, error_details, rollback_available)
VALUES (?, ?, ?, ?, ?, datetime('now'), ?, ?, ?)
`);
stmt.run(id, session_id, artifact_sys_id, deployment_type, success ? 1 : 0, agent_id, error_details || null, success ? 1 : 0);
this.logger.debug(`Recorded deployment: ${deployment_type} for ${artifact_sys_id}`);
}
catch (error) {
this.logger.error('Failed to record deployment', error);
throw error;
}
}
// Get artifact by sys_id
async getArtifact(sys_id) {
try {
const artifact = this.db.prepare(`
SELECT * FROM servicenow_artifacts WHERE sys_id = ?
`).get(sys_id);
return artifact;
}
catch (error) {
this.logger.error('Failed to get artifact', error);
return null;
}
}
// Get all artifacts for session
async getSessionArtifacts(session_id) {
try {
const artifacts = this.db.prepare(`
SELECT * FROM servicenow_artifacts
WHERE session_id = ?
ORDER BY created_at DESC
`).all(session_id);
return artifacts;
}
catch (error) {
this.logger.error('Failed to get session artifacts', error);
return [];
}
}
// Clear session data (for cleanup)
async clearSession(session_id) {
try {
this.logger.info(`Clearing session data for ${session_id}`);
// Clear all session-related data
this.db.prepare('DELETE FROM agent_coordination WHERE session_id = ?').run(session_id);
this.db.prepare('DELETE FROM agent_messages WHERE session_id = ?').run(session_id);
this.db.prepare('DELETE FROM shared_context WHERE session_id = ?').run(session_id);
this.db.prepare('DELETE FROM agent_dependencies WHERE session_id = ?').run(session_id);
this.db.prepare('DELETE FROM performance_metrics WHERE session_id = ?').run(session_id);
this.logger.info(`Cleared session data for ${session_id}`);
}
catch (error) {
this.logger.error('Failed to clear session', error);
throw error;
}
}
// Generic query method for custom queries
async query(sql, params = []) {
try {
const stmt = this.db.prepare(sql);
return stmt.all(...params);
}
catch (error) {
this.logger.error('Query failed', { sql, error });
throw error;
}
}
// Close database connection
close() {
this.db.close();
}
}
exports.MCPMemoryManager = MCPMemoryManager;
//# sourceMappingURL=mcp-memory-manager.js.map