/**
 * ╔══════════════════════════════════════════════════════════════════════╗
 * ║   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