overmind-mcp
Version:
Orchestrateur universel agents IA multi-modeles via MCP. Inclut le protocole 'Custom-Nickname' pour identifier vos agents avec des surnoms originaux (The Chaos Prophet, Shadow Sniper, etc.), l'isolation mémoire (Private Memory Context) et le support pour
110 lines • 3.85 kB
JavaScript
import fs from 'fs/promises';
import path from 'path';
import { Mutex } from 'async-mutex';
import { getWorkspaceDir } from './config.js';
import { rootLogger } from './logger.js';
const logger = rootLogger.child({ module: 'sessions' });
const sessionMutex = new Mutex();
const SESSIONS_FILE = 'bridge/agents.json';
const SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1000;
function getSessionsPath(workspaceDir) {
return path.resolve(workspaceDir || getWorkspaceDir(), SESSIONS_FILE);
}
function buildKey(runner, agentName) {
return runner ? `${runner}:${agentName}` : agentName;
}
async function purgeExpired(sessions) {
const now = Date.now();
const purged = {};
let changed = false;
for (const [key, val] of Object.entries(sessions)) {
if (typeof val === 'object' && val !== null && 'ts' in val) {
if (now - val.ts < SESSION_TTL_MS) {
purged[key] = val;
}
else {
changed = true;
}
}
else {
purged[key] = val;
}
}
return changed ? purged : sessions;
}
function extractId(val) {
if (!val)
return null;
if (typeof val === 'string')
return val;
return val.id || null;
}
export async function getLastSessionId(agentName, workspaceDir, runner) {
return sessionMutex.runExclusive(async () => {
try {
const filePath = getSessionsPath(workspaceDir);
const content = await fs.readFile(filePath, 'utf-8');
let sessions = JSON.parse(content);
sessions = await purgeExpired(sessions);
const namespacedKey = buildKey(runner, agentName);
if (sessions[namespacedKey]) {
return extractId(sessions[namespacedKey]);
}
return extractId(sessions[agentName]) || null;
}
catch (_error) {
return null;
}
});
}
export async function saveSessionId(agentName, sessionId, workspaceDir, runner) {
return sessionMutex.runExclusive(async () => {
const filePath = getSessionsPath(workspaceDir);
const dir = path.dirname(filePath);
try {
await fs.mkdir(dir, { recursive: true });
let sessions = {};
try {
const content = await fs.readFile(filePath, 'utf-8');
sessions = JSON.parse(content);
sessions = await purgeExpired(sessions);
}
catch (_e) {
// Ignore error (file new)
}
const key = buildKey(runner, agentName);
sessions[key] = { id: sessionId, ts: Date.now() };
await fs.writeFile(filePath, JSON.stringify(sessions, null, 2), 'utf-8');
}
catch (error) {
logger.error({ agentName, error }, 'Failed to save session ID.');
}
});
}
export async function deleteSessionId(agentName, workspaceDir, runner) {
return sessionMutex.runExclusive(async () => {
const filePath = getSessionsPath(workspaceDir);
try {
const content = await fs.readFile(filePath, 'utf-8');
let sessions = JSON.parse(content);
const namespacedKey = buildKey(runner, agentName);
let deleted = false;
if (sessions[namespacedKey]) {
delete sessions[namespacedKey];
deleted = true;
}
if (sessions[agentName]) {
delete sessions[agentName];
deleted = true;
}
if (deleted) {
sessions = await purgeExpired(sessions);
await fs.writeFile(filePath, JSON.stringify(sessions, null, 2), 'utf-8');
}
}
catch (_e) {
// Ignore
}
});
}
//# sourceMappingURL=sessions.js.map