UNPKG

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

145 lines • 6 kB
/** * ╔══════════════════════════════════════════════════════════════════════╗ * ║ OVERMIND BRIDGE — Helpers & Utilities ║ * ║ retry, logging, SSE parsing, validation ║ * ╚══════════════════════════════════════════════════════════════════════╝ */ /** Crée un logger simple prefixé — peut être remplacé par pino/winston */ export function createBridgeLogger(prefix) { const fmt = (level, msg, meta) => { const ts = new Date().toISOString().slice(11, 19); const metaStr = meta ? ` ${JSON.stringify(meta)}` : ''; const fn = level === 'error' ? console.error : level === 'warn' ? console.warn : console.log; fn(`[${ts}] [${prefix}] ${msg}${metaStr}`); }; return { debug: (msg, meta) => fmt('debug', msg, meta), info: (msg, meta) => fmt('info', msg, meta), warn: (msg, meta) => fmt('warn', msg, meta), error: (msg, meta) => fmt('error', msg, meta), }; } // ─── SSE Parsing ─────────────────────────────────────────────────────────── /** * Parse une réponse SSE (text/event-stream) ligne par ligne. * Retourne le premier objet JSON-RPC valide trouvé dans les lignes "data:". */ export function parseSseText(text) { for (const line of text.split('\n')) { if (line.startsWith('data: ')) { const dataStr = line.slice(6).trim(); if (dataStr) { try { return JSON.parse(dataStr); } catch { // ligne data: invalide — on continue } } } } return null; } /** * Parse le body d'une réponse MCP : SSE d'abord, fallback JSON brut. */ export function parseMcpResponseBody(text) { // 1) Essai SSE const sse = parseSseText(text); if (sse) return sse; // 2) Fallback JSON direct const trimmed = text.trim(); if (trimmed) { try { return JSON.parse(trimmed); } catch { throw new Error(`SSE response: no valid data: line. Body preview: ${trimmed.substring(0, 200)}`); } } throw new Error('SSE response: empty body'); } const DEFAULT_RETRY_CODES = ['ETIMEDOUT', 'EBODYREAD', 'ECONNRESET', 'ECONNREFUSED']; export async function withRetry(fn, opts, logger) { const codes = opts.retryableCodes ?? DEFAULT_RETRY_CODES; let lastError; for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) { try { return await fn(); } catch (err) { const error = err; lastError = error; const isRetryable = codes.includes(error?.code ?? ''); if (!isRetryable || attempt >= opts.maxAttempts) { throw err; } logger?.warn(`🔁 Retry ${attempt}/${opts.maxAttempts} (${error.code}) — delay ${opts.delayMs}ms`); await sleep(opts.delayMs); } } throw lastError; } // ─── Validation ──────────────────────────────────────────────────────────── export function validatePrompt(prompt) { if (typeof prompt !== 'string' || !prompt.trim()) { throw new Error('Prompt is required and must be a non-empty string'); } return prompt.trim(); } export function validateAgentName(name) { if (typeof name !== 'string' || !name.trim()) { throw new Error('Agent name is required and must be a non-empty string'); } const trimmed = name.trim(); if (!/^[a-zA-Z0-9_-]+$/.test(trimmed)) { throw new Error('Agent name must only contain alphanumeric, underscores, and hyphens'); } return trimmed; } // ─── Misc ────────────────────────────────────────────────────────────────── export function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } /** Extrait un sessionId depuis un marqueur textuel "SESSION_ID: xxx" */ export function extractSessionIdFromContent(content) { for (const c of content) { const m = c.text?.match(/SESSION_ID:\s*([a-zA-Z0-9_-]+)/i); if (m) return m[1]; } return undefined; } /** Formate un contexte Discord pour l'injection dans le prompt agent */ export function formatDiscordContext(params) { const { channelId, userId, username, message } = params; if (!channelId && !username) return message; return [ '[DISCORD CONTEXT]', `CHANNEL_ID: ${channelId ?? 'unknown'}`, `USER_NAME: ${username ?? 'unknown'}`, `USER_ID: ${userId ?? 'unknown'}`, '=======================', `MESSAGE: ${message}`, ].join('\n'); } /** * Interpolation de variables ${var} dans un template. * Utilisé par ScenarioLoader, PromptSource, et le CLI. * * Supporte : * - ${var} → remplacé par vars[var] si défini, sinon laissé tel quel * - ${var:-def} → valeur par défaut si vars[var] est undefined */ export function interpolate(template, vars) { if (!vars || Object.keys(vars).length === 0) return template; return template.replace(/\$\{([a-zA-Z0-9_]+)(?::-([^}]*))?\}/g, (_, key, def) => { if (vars[key] !== undefined) return vars[key]; return def !== undefined ? def : `\${${key}}`; }); } //# sourceMappingURL=utils.js.map