import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { spawn, exec } from 'child_process';
import { CONFIG, resolveConfigPath, getWorkspaceDir } from '../lib/config.js';
import { getLastSessionId, saveSessionId } from '../lib/sessions.js';
import { interpolateEnvVars } from '../lib/envUtils.js';
import { resolveModel } from '../lib/modelMapping.js';
import { withSpan } from '../lib/telemetry.js';
import { loadEnvQuietly } from '../lib/loadEnv.js';
import { registerProcess, linkSessionToPid, appendOutput, updateProcessStatus, } from '../lib/processRegistry.js';
import { registerLiveAgent, appendLiveOutput, setLiveStatus, unregisterLiveAgent, } from '../lib/agent_lifecycle.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Sur Windows, `child.kill()` ne tue que cmd.exe (le wrapper) — claude.exe
// devient orphelin et garde la session bound au token initial côté provider.
// On utilise `taskkill /F /T /PID` pour propager le kill au sous-arbre,
// puis on attend l'event 'exit' avant de respawn.
const killProcessTree = (child) => {
    return new Promise((resolve) => {
        if (!child || child.exitCode !== null || child.killed) {
            resolve();
            return;
        }
        let settled = false;
        const finish = () => {
            if (settled)
                return;
            settled = true;
            resolve();
        };
        child.once('exit', finish);
        if (process.platform === 'win32' && child.pid) {
            exec(`taskkill /F /T /PID ${child.pid}`, () => {
                // taskkill peut échouer si le process est déjà mort — on s'appuie sur 'exit'
            });
        }
        else {
            try {
                child.kill('SIGTERM');
            }
            catch {
                // Ignored
            }
            setTimeout(() => {
                if (child.exitCode === null && !child.killed) {
                    try {
                        child.kill('SIGKILL');
                    }
                    catch {
                        // Ignored
                    }
                }
            }, 2000);
        }
        // Filet de sécurité : si 'exit' ne se déclenche pas (process zombie),
        // on débloque le respawn après 5s plutôt que de bloquer indéfiniment.
        setTimeout(finish, 5000);
    });
};
export class ClaudeRunner {
    config;
    timeoutMs;
    constructor() {
        this.config = CONFIG.CLAUDE;
        this.timeoutMs = CONFIG.TIMEOUT_MS || 900000;
    }
    async runAgent(options) {
        const { prompt, agentName, autoResume } = options;
        let { sessionId } = options;
        const { CORE, PERMISSIONS, PATHS } = this.config;
        const agentCustomEnv = {};
        // Load environment variables FIRST before any processing
        const workspaceEnvPath = path.resolve(options.configPath || getWorkspaceDir(), '.env');
        loadEnvQuietly(workspaceEnvPath);
        // Also load from Workflow directory as fallback
        const workflowEnvPath = path.resolve(__dirname, '../../.env');
        loadEnvQuietly(workflowEnvPath);
        // Debug: check if variables are loaded
        if (!options.silent) {
            console.error(`[ClaudeRunner] Env check - ANTHROPIC_MODEL_Z present: ${!!process.env.ANTHROPIC_MODEL_Z}`);
            console.error(`[ClaudeRunner] Auth tokens present: ${!!process.env.ANTHROPIC_AUTH_TOKEN_Y || !!process.env.ANTHROPIC_AUTH_TOKEN_E}`);
            console.error(`[ClaudeRunner] workspaceEnvPath: ${workspaceEnvPath}`);
            console.error(`[ClaudeRunner] workflowEnvPath: ${workflowEnvPath}`);
        }
        if (agentName) {
            agentCustomEnv.OVERMIND_AGENT_NAME = agentName;
        }
        // --- Auto Resume ---
        if (autoResume && agentName && !sessionId) {
            const lastId = await getLastSessionId(agentName, options.configPath, 'claude');
            if (lastId) {
                sessionId = lastId;
                if (!options.silent) {
                    console.log(`[ClaudeRunner] Auto-resume session: ${sessionId}`);
                }
            }
        }
        let settingsPath = resolveConfigPath(PATHS.SETTINGS, options.configPath);
        if (agentName) {
            const settingsDir = path.dirname(PATHS.SETTINGS);
            const specificSettingsPath = resolveConfigPath(path.join(settingsDir, `settings_${agentName}.json`), options.configPath);
            if (!fs.existsSync(specificSettingsPath)) {
                return {
                    result: '',
                    error: `INVALID_AGENT: Agent "${agentName}" non trouvé.`,
                };
            }
            settingsPath = specificSettingsPath;
        }
        let mcpPath = resolveConfigPath(PATHS.MCP, options.configPath);
        let tmpMcpPathToDelete = null;
        let tmpSettingsPathToDelete = null;
        let customTimeoutMs = this.timeoutMs;
        if (agentName) {
            try {
                const agentSettingsPath = resolveConfigPath(path.join(path.dirname(PATHS.SETTINGS), `settings_${agentName}.json`), options.configPath);
                if (fs.existsSync(agentSettingsPath)) {
                    let settings = JSON.parse(fs.readFileSync(agentSettingsPath, 'utf8'));
                    // Debug: log environment variables
                    if (!options.silent) {
                        console.error(`[ClaudeRunner] ANTHROPIC_MODEL_Z present: ${!!process.env.ANTHROPIC_MODEL_Z}`);
                        console.error(`[ClaudeRunner] Auth tokens present: ${!!process.env.ANTHROPIC_AUTH_TOKEN_Y || !!process.env.ANTHROPIC_AUTH_TOKEN_E}`);
                        console.error(`[ClaudeRunner] ANTHROPIC_BASE_URL_Z present: ${!!process.env.ANTHROPIC_BASE_URL_Z}`);
                    }
                    // --- New interpolation logic ---
                    settings = interpolateEnvVars(settings);
                    // Debug: log interpolated values
                    if (!options.silent) {
                        console.error(`[ClaudeRunner] Interpolated ANTHROPIC_MODEL = ${settings.env?.ANTHROPIC_MODEL}`);
                        console.error(`[ClaudeRunner] Interpolated ANTHROPIC_BASE_URL = ${settings.env?.ANTHROPIC_BASE_URL}`);
                    }
                    // 1. Create a temporary settings file with interpolated values
                    const tmpSettingsPath = path.join(path.dirname(agentSettingsPath), `settings_${agentName}_tmp.json`);
                    fs.writeFileSync(tmpSettingsPath, JSON.stringify(settings, null, 2));
                    settingsPath = tmpSettingsPath;
                    tmpSettingsPathToDelete = tmpSettingsPath;
                    if (settings.env) {
                        Object.assign(agentCustomEnv, settings.env);
                        if (settings.env.AGENT_TIMEOUT_MS || settings.env.API_TIMEOUT_MS) {
                            const timeoutValue = settings.env.AGENT_TIMEOUT_MS || settings.env.API_TIMEOUT_MS;
                            customTimeoutMs = parseInt(timeoutValue, 10) || customTimeoutMs;
                        }
                        if (!options.model && settings.env.MODEL) {
                            agentCustomEnv.ANTHROPIC_MODEL = settings.env.MODEL;
                        }
                    }
                    const agentMcpPath = resolveConfigPath(path.join(path.dirname(PATHS.SETTINGS), `.mcp.${agentName}.json`));
                    if (fs.existsSync(agentMcpPath)) {
                        mcpPath = agentMcpPath;
                    }
                    else if (settings.enableAllProjectMcpServers === false &&
                        Array.isArray(settings.enabledMcpjsonServers)) {
                        if (fs.existsSync(mcpPath)) {
                            const fullMcp = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
                            const filteredMcp = { mcpServers: {} };
                            for (const serverName of settings.enabledMcpjsonServers) {
                                if (fullMcp.mcpServers && fullMcp.mcpServers[serverName]) {
                                    filteredMcp.mcpServers[serverName] = fullMcp.mcpServers[serverName];
                                }
                            }
                            const tmpMcpPath = path.join(path.dirname(agentSettingsPath), `mcp_${agentName}_tmp.json`);
                            fs.writeFileSync(tmpMcpPath, JSON.stringify(filteredMcp, null, 2));
                            mcpPath = tmpMcpPath;
                            tmpMcpPathToDelete = tmpMcpPath;
                        }
                    }
                }
            }
            catch (e) {
                console.error(`[ClaudeRunner] [WARN] Error processing agent settings: ${e}`);
            }
        }
        const argsSpawn = [];
        if (CORE)
            argsSpawn.push(...CORE.split(' ').filter(Boolean));
        if (PERMISSIONS)
            argsSpawn.push(...PERMISSIONS.split(' ').filter(Boolean));
        argsSpawn.push('--settings', settingsPath);
        argsSpawn.push('--mcp-config', mcpPath);
        argsSpawn.push('--output-format', 'json');
        let modelUsed = options.model;
        if (!modelUsed && agentCustomEnv.ANTHROPIC_MODEL) {
            modelUsed = agentCustomEnv.ANTHROPIC_MODEL;
        }
        // Remember original value (nickname or raw model) for display
        const originalModel = modelUsed ?? '';
        // Resolve nickname → real model ID before calling the API
        if (modelUsed) {
            modelUsed = resolveModel(modelUsed);
        }
        if (sessionId)
            argsSpawn.push('--resume', sessionId);
        if (modelUsed)
            argsSpawn.push('--model', modelUsed);
        if (agentName)
            argsSpawn.push('--name', agentName);
        // ───────────────────────────────────────────────────────────────────────────
        // 🔄 FALLBACK TOKEN RETRY LOGIC
        //
        // Overmind lit les tokens fallback depuis agentCustomEnv (résolus depuis $VAR).
        // Si une erreur 401 (auth) survient, on tente chaque fallback séquentiellement :
        //   AUTH_FALLBACK_1 → AUTH_FALLBACK_2 → AUTH_FALLBACK_3
        //
        // Settings exemple :
        //   { "env": { "ANTHROPIC_AUTH_TOKEN": "$ANTHROPIC_AUTH_FALLBACK_1" } }
        // ───────────────────────────────────────────────────────────────────────────
        const FALLBACK_KEYS = ['AUTH_FALLBACK_1', 'AUTH_FALLBACK_2', 'AUTH_FALLBACK_3'];
        const TOKEN_KEYS = ['ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_AUTH_TOKEN_E'];
        /**
         * Vérifie si une erreur est retryable (fallback recommended).
         * 401 = auth error (token invalide/expiré)
         * 429 = rate limit / quota exhausted
         * 500/502/503 = server error
         */
        const isRetryableError = (stderr, jsonEnv) => {
            const lower = stderr.toLowerCase();
            const status = jsonEnv?.api_error_status;
            if (status === 401)
                return true;
            if (status === 429)
                return true;
            if (status === 500 || status === 502 || status === 503)
                return true;
            return (lower.includes('401') ||
                lower.includes('unauthorized') ||
                lower.includes('invalid api key') ||
                lower.includes('api key invalid') ||
                lower.includes('authentication failed') ||
                lower.includes('auth error') ||
                lower.includes('invalid authentication') ||
                lower.includes('429') ||
                lower.includes('rate limit') ||
                lower.includes('quota exhausted') ||
                lower.includes('limit exhausted') ||
                lower.includes('503') ||
                lower.includes('service unavailable') ||
                lower.includes('500') ||
                lower.includes('internal server error'));
        };
        /**
         * Extrait les tokens fallback disponibles depuis agentCustomEnv.
         * Retourne un tableau de { key, value } pour chaque fallback non vide.
         */
        const getAvailableFallbacks = () => {
            const fallbacks = [];
            for (const key of FALLBACK_KEYS) {
                const val = agentCustomEnv[key];
                if (val && typeof val === 'string' && val.length > 0) {
                    fallbacks.push({ key, value: val });
                }
            }
            return fallbacks;
        };
        /**
         * Détermine quel token utiliser selon l'index de retry.
         * - index 0 = tentative initiale → use primary token (ANTHROPIC_AUTH_TOKEN)
         * - index 1+ = retry → skip primary, use fallbacks directly
         */
        const getTokenForIndex = (index) => {
            if (index === 0) {
                // Tentative initiale : utiliser le primary token
                // NOTE: si la valeur est un $VAR non résolu (interpolateEnvVars n'a pas trouvé
                // la variable dans process.env à ce moment), on le passe quand même à spawnWithToken
                // qui fera la résolution finale via process.env.
                for (const tk of TOKEN_KEYS) {
                    const val = agentCustomEnv[tk];
                    if (val && typeof val === 'string' && val.length > 0) {
                        return { tokenEnvKey: tk, tokenValue: val };
                    }
                }
                // Aucun primary token trouvé — retourner null plutôt que de tomber dans les fallbacks
                return null;
            }
            // Retry (index >= 1) : skip primary, use fallbacks directly
            const fallbacks = getAvailableFallbacks();
            const fallbackIndex = index - 1; // index 1 → fallback[0] (AUTH_FALLBACK_1)
            if (fallbackIndex < fallbacks.length) {
                return { tokenEnvKey: fallbacks[fallbackIndex].key, tokenValue: fallbacks[fallbackIndex].value };
            }
            return null;
        };
        // ─── AbortSignal support ─────────────────────────────────────────────────────
        let currentChildRef = null;
        if (options.signal?.aborted) {
            return Promise.reject(new Error('ABORTED'));
        }
        options.signal?.addEventListener('abort', () => {
            if (currentChildRef)
                void killProcessTree(currentChildRef);
        });
        const runImpl = async (span) => {
            span.setAttribute('agentName', agentName || '');
            span.setAttribute('model', modelUsed || '');
            span.setAttribute('runner', 'claude');
            return new Promise((resolve) => {
                let resolved = false;
                let retryCount = 0;
                const maxRetries = getAvailableFallbacks().length + 1; // primary + fallbacks
                currentChildRef = null;
                let currentStderr = '';
                let currentStdout = '';
                const MAX_BUF = 10 * 1024 * 1024;
                let currentSessionId = sessionId;
                let earlyExitTriggered = false; // Prevent double-exit on early retry
                const safeResolve = (value) => {
                    if (!resolved) {
                        resolved = true;
                        resolve(value);
                    }
                };
                const triggerRetry = async (targetRetryCount) => {
                    if (earlyExitTriggered)
                        return;
                    earlyExitTriggered = true;
                    if (hardTimeoutTimer)
                        clearTimeout(hardTimeoutTimer);
                    if (killTimer)
                        clearTimeout(killTimer);
                    // Attendre la mort effective de l'arbre (cmd.exe + claude.exe sur Win)
                    // avant de respawn, sinon le nouveau process tape sur la même session
                    // encore "vivante" côté provider → 429 fantôme.
                    if (currentChildRef) {
                        await killProcessTree(currentChildRef);
                    }
                    retryCount = targetRetryCount;
                    const tokenInfo = getTokenForIndex(retryCount);
                    if (!options.silent) {
                        process.stderr.write(`\n\x1b[41m\x1b[37m[ClaudeRunner] 🔄 Retry ${retryCount}/${maxRetries} avec ${tokenInfo?.tokenEnvKey || 'UNKNOWN'}...\x1b[0m\n`);
                    }
                    setImmediate(() => spawnWithToken(tokenInfo));
                };
                const cleanupTmpFiles = () => {
                    if (tmpMcpPathToDelete && fs.existsSync(tmpMcpPathToDelete)) {
                        try {
                            fs.unlinkSync(tmpMcpPathToDelete);
                        }
                        catch {
                            // Ignored
                        }
                    }
                    if (tmpSettingsPathToDelete && fs.existsSync(tmpSettingsPathToDelete)) {
                        try {
                            fs.unlinkSync(tmpSettingsPathToDelete);
                        }
                        catch {
                            // Ignored
                        }
                    }
                };
                let killTimer = null;
                let hardTimeoutTimer = null;
                /**
                 * Fonction centrale qui spawn le processus Claude avec le bon token.
                 * Appelé initialement et après chaque retry.
                 */
                const spawnWithToken = (tokenInfo) => {
                    // Nettoyer les listeners/timers de la tentative précédente
                    if (hardTimeoutTimer) {
                        clearTimeout(hardTimeoutTimer);
                        hardTimeoutTimer = null;
                    }
                    if (killTimer) {
                        clearTimeout(killTimer);
                        killTimer = null;
                    }
                    // Construire l'env avec le bon token
                    // NOTE: Overmind gère la substitution des variables $VAR dans les settings.
                    // Les fallback tokens (AUTH_FALLBACK_1/2/3) sont résolus ici pour le retry 401.
                    const spawnEnv = {
                        ...process.env,
                        ...agentCustomEnv,
                    };
                    if (tokenInfo) {
                        // Remplacer le token actif par celui du fallback
                        // NOTE: Les tokens peuvent encore contenir des $VAR non résolus
                        // (interpolateEnvVars n'a pas trouvé ces vars dans process.env au moment du load).
                        // On résout ici via process.env (qui a été peuplé par loadEnvQuietly).
                        for (const tk of TOKEN_KEYS) {
                            delete spawnEnv[tk];
                        }
                        let resolvedToken = tokenInfo.tokenValue;
                        if (resolvedToken.startsWith('$')) {
                            const envKey = resolvedToken.slice(1);
                            resolvedToken = process.env[envKey] || resolvedToken;
                        }
                        // Le Claude CLI lit ANTHROPIC_AUTH_TOKEN — on injecte toujours sous ce nom,
                        // peu importe que tokenInfo vienne du primary ou d'un AUTH_FALLBACK_n.
                        spawnEnv['ANTHROPIC_AUTH_TOKEN'] = resolvedToken;
                    }
                    currentStderr = '';
                    currentStdout = '';
                    const command = process.platform === 'win32' ? 'cmd.exe' : 'claude';
                    const spawnArgs = process.platform === 'win32'
                        ? ['/c', 'claude', ...argsSpawn, '-p']
                        : ['claude', ...argsSpawn, '-p'];
                    if (!options.silent) {
                        const tokenLabel = tokenInfo ? ` (token: ${tokenInfo.tokenEnvKey})` : '';
                        process.stderr.write(`\n\x1b[33m[ClaudeRunner]${tokenLabel} Spawning Claude CLI...\x1b[0m\n`);
                    }
                    currentChildRef = spawn(command, spawnArgs, {
                        cwd: options.cwd || process.cwd(),
                        windowsHide: true,
                        env: spawnEnv,
                        shell: false,
                        signal: options.signal,
                    });
                    // Register: disk persistence + in-memory lifecycle
                    if (currentChildRef.pid) {
                        const pid = currentChildRef.pid;
                        void registerProcess(pid, {
                            agentName: agentName || '',
                            runner: 'claude',
                            configPath: options.configPath,
                        });
                        registerLiveAgent({
                            pid,
                            runner: 'claude',
                            agentName: agentName || '',
                            sessionId: currentSessionId || '',
                            cleanupFn: async () => {
                                if (currentChildRef)
                                    await killProcessTree(currentChildRef);
                            },
                            childRef: currentChildRef,
                        });
                        // Cleanup on exit: lifecycle state only (setLiveStatus + unregister).
                        // processRegistry.updateProcessStatus is handled by the 'close' handler below.
                        currentChildRef.once('exit', (code) => {
                            const exitCode = code ?? null;
                            setLiveStatus(pid, code === 0 ? 'done' : 'failed', exitCode);
                            unregisterLiveAgent(pid);
                        });
                    }
                    if (currentChildRef.stdout) {
                        currentChildRef.stdout.on('data', (d) => {
                            const chunk = d.toString();
                            if (currentChildRef && currentChildRef.pid && chunk) {
                                appendLiveOutput(currentChildRef.pid, chunk);
                                void appendOutput(currentChildRef.pid, chunk, options.configPath);
                            }
                            if (currentStdout.length + chunk.length > MAX_BUF)
                                currentStdout = currentStdout.slice(-MAX_BUF);
                            else
                                currentStdout += chunk;
                            if (agentName && !options.silent) {
                                process.stderr.write(`[ClaudeRunner:${agentName}] ${chunk}`);
                            }
                        });
                    }
                    if (currentChildRef.stderr) {
                        currentChildRef.stderr.on('data', (d) => {
                            const chunk = d.toString();
                            if (currentChildRef && currentChildRef.pid && chunk) {
                                appendLiveOutput(currentChildRef.pid, chunk);
                                void appendOutput(currentChildRef.pid, chunk, options.configPath);
                            }
                            if (currentStderr.length + chunk.length > MAX_BUF)
                                currentStderr = currentStderr.slice(-MAX_BUF);
                            else
                                currentStderr += chunk;
                            if (agentName && !options.silent) {
                                process.stderr.write(`[ClaudeRunner:${agentName}:ERR] ${chunk}`);
                            }
                        });
                    }
                    if (currentChildRef.stdin) {
                        currentChildRef.stdin.write(prompt);
                        currentChildRef.stdin.end();
                    }
                    const timeout = setTimeout(() => {
                        if (currentChildRef && currentChildRef.stdin && !currentChildRef.stdin.destroyed) {
                            try {
                                currentChildRef.stdin.write('\n');
                                if (!options.silent) {
                                    process.stderr.write(`\n\x1b[33m[ClaudeRunner] [WARN] Agent stagnant (${customTimeoutMs}ms). Envoi d'un keep-alive (\\n)...\x1b[0m\n`);
                                }
                            }
                            catch (_e) {
                                // Ignore
                            }
                        }
                        const hardTimeoutDelay = CONFIG.HARD_TIMEOUT_MS || 60000;
                        hardTimeoutTimer = setTimeout(() => {
                            if (currentChildRef)
                                void killProcessTree(currentChildRef);
                            cleanupTmpFiles();
                            safeResolve({
                                result: '',
                                error: 'HARD_TIMEOUT',
                                rawOutput: currentStdout + currentStderr,
                            });
                        }, hardTimeoutDelay);
                    }, customTimeoutMs);
                    currentChildRef.on('error', (err) => {
                        clearTimeout(timeout);
                        if (hardTimeoutTimer)
                            clearTimeout(hardTimeoutTimer);
                        cleanupTmpFiles();
                        safeResolve({ result: '', error: `SPAWN_ERROR: ${err.message}`, rawOutput: '' });
                    });
                    currentChildRef.on('close', async (code) => {
                        clearTimeout(timeout);
                        if (hardTimeoutTimer)
                            clearTimeout(hardTimeoutTimer);
                        const fullRaw = currentStdout + (currentStderr ? `\n\n--- STDERR ---\n${currentStderr}` : '');
                        // ─── Parser le JSON en premier (pour extraire api_error_status) ───
                        let jsonEnvelope = null;
                        const trimmedStdout = currentStdout.trim();
                        try {
                            jsonEnvelope = JSON.parse(trimmedStdout);
                        }
                        catch {
                            const lastBrace = trimmedStdout.lastIndexOf('}');
                            const firstBrace = trimmedStdout.lastIndexOf('{', lastBrace);
                            if (firstBrace !== -1 && lastBrace !== -1) {
                                try {
                                    jsonEnvelope = JSON.parse(trimmedStdout.substring(firstBrace, lastBrace + 1));
                                }
                                catch {
                                    // Ignored
                                }
                            }
                        }
                        // ─── Fallback retry ─────────────────────────────────────────────────
                        // Sur 401/429/5xx, on tue l'arbre du process courant via killProcessTree
                        // (taskkill /F /T sur Windows pour ne pas laisser claude.exe orphelin),
                        // puis on respawn un nouveau noeud avec --resume + le token fallback
                        // suivant (AUTH_FALLBACK_1 → 2 → 3).
                        // ───────────────────────────────────────────────────────────────────
                        const FALLBACK_RETRY_ENABLED = true;
                        const isRetryable = isRetryableError(currentStderr, jsonEnvelope);
                        const hasRetryableStatus = jsonEnvelope !== null &&
                            (jsonEnvelope.api_error_status === 401 ||
                                jsonEnvelope.api_error_status === 429 ||
                                jsonEnvelope.api_error_status === 500 ||
                                jsonEnvelope.api_error_status === 502 ||
                                jsonEnvelope.api_error_status === 503);
                        const isFailure = FALLBACK_RETRY_ENABLED && ((code !== 0 && isRetryable) || hasRetryableStatus);
                        if (isFailure) {
                            if (retryCount < maxRetries) {
                                triggerRetry(retryCount + 1);
                                return;
                            }
                            else {
                                if (!options.silent) {
                                    process.stderr.write(`\n\x1b[41m\x1b[37m[ClaudeRunner] ❌ Tous les tokens fallback épuisés. Error retryable finale.\x1b[0m\n`);
                                }
                                // Status updated by the 'exit' handler registered above
                                safeResolve({
                                    result: '',
                                    error: 'RETRYABLE_ERROR_ALL_FALLBACKS_EXHAUSTED',
                                    rawOutput: fullRaw,
                                });
                                return;
                            }
                        }
                        cleanupTmpFiles();
                        try {
                            if (jsonEnvelope) {
                                let foundSessionId = currentSessionId;
                                if (jsonEnvelope.session_id && agentName) {
                                    foundSessionId = jsonEnvelope.session_id;
                                    currentSessionId = foundSessionId;
                                    await saveSessionId(agentName, jsonEnvelope.session_id, options.configPath, 'claude');
                                    if (currentChildRef?.pid) {
                                        void linkSessionToPid(jsonEnvelope.session_id, currentChildRef.pid, options.configPath);
                                    }
                                }
                                if (currentChildRef?.pid) {
                                    void updateProcessStatus(currentChildRef.pid, code === 0 ? 'done' : 'failed', code ?? null, options.configPath);
                                }
                                return safeResolve({
                                    result: jsonEnvelope.reply ||
                                        jsonEnvelope.result ||
                                        currentStdout.trim(),
                                    sessionId: foundSessionId,
                                    rawOutput: currentStdout,
                                    model: modelUsed ?? undefined,
                                    nickname: originalModel !== modelUsed ? originalModel : undefined,
                                });
                            }
                            if (code === 0) {
                                if (currentChildRef?.pid) {
                                    void updateProcessStatus(currentChildRef.pid, 'done', code, options.configPath);
                                }
                                return safeResolve({
                                    result: currentStdout.trim(),
                                    sessionId: currentSessionId,
                                    rawOutput: currentStdout,
                                    model: modelUsed ?? undefined,
                                    nickname: originalModel !== modelUsed ? originalModel : undefined,
                                });
                            }
                            if (currentChildRef?.pid) {
                                void updateProcessStatus(currentChildRef.pid, 'failed', code, options.configPath);
                            }
                            safeResolve({
                                result: '',
                                error: code !== 0 ? `EXIT_CODE_${code}` : 'JSON_PARSE_ERROR',
                                rawOutput: fullRaw,
                            });
                        }
                        catch (error) {
                            safeResolve({
                                result: '',
                                error: `INTERNAL_ERROR: ${error instanceof Error ? error.message : String(error)}`,
                                rawOutput: fullRaw,
                            });
                        }
                    });
                };
                // ─── Démarrage initial avec le primary token ───
                spawnWithToken(getTokenForIndex(0));
            });
        };
        return withSpan('claude.runAgent', runImpl, {
            agentName: agentName || '',
            model: modelUsed || '',
            runner: 'claude',
        });
    }
}
//# sourceMappingURL=ClaudeRunner.js.map