import { existsSync, readFileSync, readdirSync, writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';
import { homedir } from 'node:os';
import { UnifiedOrchestrator } from './unifiedOrchestrator.js';
import { ParallelExecutor, createTask } from './parallelExecutor.js';
import { DEFAULT_HUMAN_REWARD_WEIGHTS, runDualTournament, } from './dualTournament.js';
import { resolveWinner as resolveWinnerStrategy } from './winnerStrategy.js';
export const DEFAULT_REWARD_WEIGHTS = {
    executionSuccess: 0.25,
    testsPassed: 0.30,
    staticAnalysis: 0.15,
    codeQuality: 0.10,
    blastRadius: 0.10,
    selfAssessment: 0.05,
    speedBonus: 0.05,
};
/**
 * Calculate composite reward score from individual signals.
 */
export function calculateRewardScore(signals, weights = DEFAULT_REWARD_WEIGHTS) {
    let score = 0;
    let totalWeight = 0;
    for (const [key, weight] of Object.entries(weights)) {
        const signal = signals[key];
        if (typeof signal === 'number') {
            score += signal * weight;
            totalWeight += weight;
        }
    }
    return totalWeight > 0 ? score / totalWeight : 0;
}
/**
 * Extract reward signals from execution output text.
 */
export function extractRewardSignals(output, durationMs, baselineDurationMs = 60000) {
    const lower = output.toLowerCase();
    const signals = {};
    const clamp = (value) => Math.max(0, Math.min(1, value));
    // Execution success - no error keywords
    const hasErrors = /\b(error|exception|failed|failure|fatal)\b/i.test(output);
    signals.executionSuccess = hasErrors ? 0 : 1;
    // Tests passed - look for test result patterns
    const testMatch = output.match(/(\d+)\s*(?:tests?\s*)?pass(?:ed|ing)?/i);
    const failMatch = output.match(/(\d+)\s*(?:tests?\s*)?fail(?:ed|ing)?/i);
    if (testMatch || failMatch) {
        const passed = parseInt(testMatch?.[1] ?? '0', 10);
        const failed = parseInt(failMatch?.[1] ?? '0', 10);
        const total = passed + failed;
        signals.testsPassed = total > 0 ? passed / total : (/pass|success|✓/i.test(lower) ? 1 : 0.5);
    }
    else if (/all tests pass|tests passing|✓.*test/i.test(lower)) {
        signals.testsPassed = 1;
    }
    // Static analysis - lint/type check patterns
    if (/lint.*pass|no.*lint.*error|eslint.*clean|tsc.*success/i.test(lower)) {
        signals.staticAnalysis = 1;
    }
    else if (/lint.*warn|eslint.*warn/i.test(lower)) {
        signals.staticAnalysis = 0.7;
    }
    else if (/lint.*error|type.*error|eslint.*error/i.test(lower)) {
        signals.staticAnalysis = 0.3;
    }
    // Code quality heuristics (TODO/HACK penalties vs cleanup/refactor bonuses)
    if (/todo|hack|workaround|temporary fix/i.test(lower)) {
        signals.codeQuality = 0.45;
    }
    else if (/refactor|cleanup|simplif|reducing complexity|streamlined/i.test(lower)) {
        signals.codeQuality = Math.max(signals.codeQuality ?? 0, 0.65);
    }
    // Blast radius approximation from diff summaries / dependency adds
    const diffMatch = output.match(/(\d+)\s+files?\s+changed/i);
    if (diffMatch) {
        const filesChanged = parseInt(diffMatch[1] ?? '0', 10);
        signals.blastRadius = clamp(1 - Math.min(1, filesChanged / 8));
    }
    if (/npm install|yarn add|pnpm add|pip install|go get|cargo add/i.test(lower)) {
        signals.blastRadius = clamp((signals.blastRadius ?? 0.6) - 0.15);
    }
    // Self-assessment from confidence keywords
    if (/confident|verified|validated|confirmed/i.test(lower)) {
        signals.selfAssessment = 0.9;
    }
    else if (/likely|probably|should work/i.test(lower)) {
        signals.selfAssessment = 0.6;
    }
    else if (/uncertain|unclear|might|may/i.test(lower)) {
        signals.selfAssessment = 0.3;
    }
    // Speed bonus - faster than baseline gets bonus
    if (durationMs > 0 && baselineDurationMs > 0) {
        const speedRatio = baselineDurationMs / durationMs;
        signals.speedBonus = Math.min(1, Math.max(0, (speedRatio - 0.5) / 1.5));
    }
    return signals;
}
export const REPO_UPGRADE_MODE_DEFINITIONS = {
    'single-continuous': {
        id: 'single-continuous',
        label: 'Single continuous',
        description: 'Single-pass deterministic execution focused on minimal blast radius.',
        variants: ['primary'],
        variantGuidance: {
            primary: 'Plan and execute the best possible upgrade in one pass. Keep edits surgical and runnable.',
        },
        parallelVariants: false,
    },
    'dual-rl-continuous': {
        id: 'dual-rl-continuous',
        label: 'Dual-agent RL continuous',
        description: 'Primary + refiner loop where the refiner competes to improve safety and quality. Refiner sees primary output.',
        variants: ['primary', 'refiner'],
        variantGuidance: {
            primary: 'Primary pass sets the baseline plan and changes with conservative scope and runnable checks.',
            refiner: 'RL refiner critiques the primary, fixes gaps, tightens safety, and only repeats steps if they materially improve the result.',
        },
        refinerBias: 0.05,
        parallelVariants: false, // Sequential so refiner can see primary's work
    },
    'dual-rl-tournament': {
        id: 'dual-rl-tournament',
        label: 'Dual-agent RL tournament',
        description: 'Primary and refiner compete in parallel with isolated workspaces. Best result wins per step.',
        variants: ['primary', 'refiner'],
        variantGuidance: {
            primary: 'Execute the upgrade with focus on correctness and test coverage. You are competing against another agent.',
            refiner: 'Execute the upgrade with focus on safety and minimal changes. You are competing against another agent.',
        },
        refinerBias: 0.03, // Lower bias since both start fresh
        parallelVariants: true, // Run in parallel with git worktree isolation
    },
};
/**
 * Build a repo-wide upgrade plan using common directories. Falls back to a single
 * catch-all module when no known scopes are found.
 */
export function buildRepoWidePlan(workspaceRoot, additionalScopes = []) {
    const areas = [
        {
            id: 'source',
            label: 'Source',
            description: 'Core application and library code.',
            scope: ['src/**/*', 'lib/**/*'],
            candidates: ['src', 'lib'],
            codemodCommands: ['npx jscodeshift -t <transform> src'],
            validationCommands: ['npm run build', 'npm test'],
        },
        {
            id: 'tests',
            label: 'Tests',
            description: 'Unit, integration, and smoke tests.',
            scope: ['test/**/*', '__tests__/**/*'],
            candidates: ['test', '__tests__'],
            validationCommands: ['npm test -- --runInBand', 'npm run lint'],
        },
        {
            id: 'docs',
            label: 'Docs',
            description: 'Documentation and guides.',
            scope: ['docs/**/*', 'README.md'],
            candidates: ['docs', 'README.md'],
        },
        {
            id: 'scripts',
            label: 'Tooling Scripts',
            description: 'Build, migration, and maintenance scripts.',
            scope: ['scripts/**/*'],
            candidates: ['scripts'],
            validationCommands: ['npm run lint -- scripts/**/*.ts'],
        },
        {
            id: 'agents',
            label: 'Agents',
            description: 'Agent rules and behaviors.',
            scope: ['agents/**/*'],
            candidates: ['agents'],
            validationCommands: ['npm run build'],
        },
        {
            id: 'skills',
            label: 'Skills',
            description: 'Skill packs and reusable automation.',
            scope: ['skills/**/*'],
            candidates: ['skills'],
            validationCommands: ['npm run build'],
        },
        {
            id: 'examples',
            label: 'Examples',
            description: 'Example flows and templates.',
            scope: ['examples/**/*'],
            candidates: ['examples'],
        },
        {
            id: 'configs',
            label: 'Configuration',
            description: 'Configuration and deployment settings.',
            scope: ['config/**/*', '*.config.*', '*.rc', '*.json'],
            candidates: ['config'],
        },
    ];
    for (const extra of additionalScopes) {
        if (!extra?.trim())
            continue;
        const slug = extra.replace(/[^\w]/g, '-').toLowerCase() || 'custom';
        areas.push({
            id: `custom-${slug}`,
            label: `Custom: ${extra}`,
            description: `User-specified scope: ${extra}`,
            scope: [`${extra}/**/*`],
            candidates: [extra],
        });
    }
    const modules = [];
    const workspaceModules = detectWorkspaceModules(workspaceRoot);
    modules.push(...workspaceModules);
    for (const area of areas) {
        const matches = area.candidates.some((candidate) => existsSync(join(workspaceRoot, candidate)));
        if (!matches) {
            continue;
        }
        modules.push({
            id: area.id,
            label: area.label,
            description: area.description,
            scope: area.scope,
            codemodCommands: area.codemodCommands,
            validationCommands: area.validationCommands,
            steps: buildDefaultSteps(area.id),
        });
    }
    if (modules.length === 0) {
        modules.push({
            id: 'repo-wide',
            label: 'Repository',
            description: 'Fallback repo-wide upgrade module.',
            scope: ['**/*'],
            steps: buildDefaultSteps('repo'),
        });
    }
    return { modules };
}
function detectWorkspaceModules(workspaceRoot) {
    const pkgPath = join(workspaceRoot, 'package.json');
    if (!existsSync(pkgPath)) {
        return [];
    }
    let workspaces = [];
    try {
        const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
        workspaces = parseWorkspaceField(pkg?.workspaces);
    }
    catch {
        return [];
    }
    if (!workspaces.length) {
        return [];
    }
    const modules = [];
    const seen = new Set();
    for (const pattern of workspaces) {
        const paths = expandWorkspacePattern(workspaceRoot, pattern);
        for (const relPath of paths) {
            if (seen.has(relPath)) {
                continue;
            }
            seen.add(relPath);
            const cleanPath = relPath.replace(/\/+$/, '');
            const name = cleanPath.split('/').filter(Boolean).pop() ?? cleanPath;
            const normalizedPath = cleanPath.replace(/[/\\]+/g, '-');
            const slug = `workspace-${normalizedPath.replace(/[^\w-]+/g, '-').replace(/-+/g, '-').toLowerCase() || 'default'}`;
            modules.push({
                id: slug,
                label: `Workspace: ${name}`,
                description: `Upgrade tasks scoped to workspace "${name}" (${cleanPath}).`,
                scope: [`${cleanPath}/**/*`],
                validationCommands: [
                    `npm test --workspace ${name} --if-present`,
                    `npm run lint --workspace ${name} --if-present`,
                ],
                steps: buildDefaultSteps(slug),
            });
        }
    }
    return modules;
}
function parseWorkspaceField(value) {
    if (Array.isArray(value)) {
        return value.map((entry) => (typeof entry === 'string' ? entry.trim() : '')).filter(Boolean);
    }
    if (value && typeof value === 'object') {
        const record = value;
        if (Array.isArray(record.packages)) {
            return record.packages.map((entry) => (typeof entry === 'string' ? entry.trim() : '')).filter(Boolean);
        }
    }
    return [];
}
function expandWorkspacePattern(workspaceRoot, pattern) {
    const normalized = pattern.trim();
    if (!normalized)
        return [];
    if (!normalized.includes('*')) {
        const target = join(workspaceRoot, normalized);
        return existsSync(target) ? [normalized] : [];
    }
    const base = normalized.split('*')[0]?.replace(/\/+$/, '') ?? '';
    if (!base)
        return [];
    const baseDir = join(workspaceRoot, base);
    if (!existsSync(baseDir))
        return [];
    try {
        const entries = readdirSync(baseDir, { withFileTypes: true });
        return entries.filter((entry) => entry.isDirectory()).map((entry) => `${base}/${entry.name}`);
    }
    catch {
        return [];
    }
}
export class RepoUpgradeOrchestrator extends UnifiedOrchestrator {
    executor;
    variantWorkspaceRoots;
    repoPolicy;
    parallelExecutor = null;
    objective;
    static repoTypeTelemetry = new Map();
    /** Disable persisted telemetry via env or flag */
    static disableTelemetryPersistence = process.env.AGI_DISABLE_RL_TELEMETRY === '1';
    constructor(executor) {
        super();
        this.executor = executor;
    }
    /**
     * Get or create a parallel executor instance
     */
    getParallelExecutor(concurrency) {
        if (!this.parallelExecutor) {
            this.parallelExecutor = new ParallelExecutor({
                maxConcurrency: concurrency,
                continueOnFailure: true,
                onTaskEvent: (event) => {
                    this.emit({
                        type: `parallel.${event.type}`,
                        timestamp: event.timestamp,
                        data: event.data,
                    });
                },
            });
        }
        return this.parallelExecutor;
    }
    /**
     * Execute the repo-wide plan using either single continuous or dual RL continuous mode.
     * Supports parallel module processing and parallel variant execution for improved performance.
     */
    async run(plan, options) {
        const mode = options.mode;
        const continueOnFailure = options.continueOnFailure ?? true;
        const objective = options.objective ?? 'Repository-wide source code upgrade';
        const modeDefinition = getModeDefinition(mode);
        this.objective = objective;
        const parallelModules = options.parallelModules ?? false;
        const parallelModuleConcurrency = options.parallelModuleConcurrency ?? 3;
        const parallelVariants = options.parallelVariants ?? true;
        this.variantWorkspaceRoots = options.variantWorkspaceRoots;
        this.repoPolicy = options.repoPolicy ?? undefined;
        // Reset state per run to keep reports clean
        this.results = [];
        this.findings = [];
        this.parallelExecutor = null;
        const variantStats = { primaryWins: 0, refinerWins: 0, ties: 0, totalSteps: 0 };
        // Emit parallel mode info
        this.emit({
            type: 'upgrade.parallel.config',
            timestamp: Date.now(),
            data: {
                parallelModules,
                parallelModuleConcurrency,
                parallelVariants,
                mode,
            },
        });
        let moduleReports;
        if (parallelModules && plan.modules.length > 1) {
            // PARALLEL MODULE PROCESSING
            moduleReports = await this.runModulesInParallel(plan.modules, mode, modeDefinition, parallelModuleConcurrency, parallelVariants, continueOnFailure, variantStats);
        }
        else {
            // SEQUENTIAL MODULE PROCESSING (original behavior)
            moduleReports = await this.runModulesSequentially(plan.modules, mode, modeDefinition, parallelVariants, continueOnFailure, variantStats);
        }
        const baseReport = this.generateReport(objective);
        return {
            ...baseReport,
            mode,
            continueOnFailure,
            modules: moduleReports,
            variantStats,
            variantWorkspaceRoots: this.variantWorkspaceRoots,
            repoPolicy: this.repoPolicy,
        };
    }
    /**
     * Run modules sequentially (original behavior)
     */
    async runModulesSequentially(modules, mode, modeDefinition, parallelVariants, continueOnFailure, variantStats) {
        const moduleReports = [];
        let halted = false;
        for (const module of modules) {
            if (halted) {
                moduleReports.push({
                    id: module.id,
                    label: module.label,
                    scope: module.scope,
                    steps: [],
                    status: 'skipped',
                });
                continue;
            }
            const moduleReport = await this.processModule(module, mode, modeDefinition, parallelVariants, variantStats, continueOnFailure);
            moduleReports.push(moduleReport);
            if (moduleReport.status === 'failed' && !continueOnFailure) {
                halted = true;
            }
        }
        return moduleReports;
    }
    /**
     * Run modules in parallel using the ParallelExecutor
     */
    async runModulesInParallel(modules, mode, modeDefinition, concurrency, parallelVariants, continueOnFailure, variantStats) {
        const executor = this.getParallelExecutor(concurrency);
        this.emit({
            type: 'upgrade.parallel.start',
            timestamp: Date.now(),
            data: { moduleCount: modules.length, concurrency },
        });
        // Create parallel tasks for each module
        const tasks = modules.map((module) => createTask(`module:${module.id}`, async () => this.processModule(module, mode, modeDefinition, parallelVariants, variantStats, continueOnFailure), {
            label: module.label,
            parallelizable: true,
            group: 'modules',
        }));
        const batchResult = await executor.execute(tasks);
        this.emit({
            type: 'upgrade.parallel.complete',
            timestamp: Date.now(),
            data: {
                totalDurationMs: batchResult.totalDurationMs,
                successCount: batchResult.successCount,
                failureCount: batchResult.failureCount,
                parallelismAchieved: batchResult.parallelismAchieved,
            },
        });
        // Extract results in order, handling failures
        const moduleReports = [];
        for (const result of batchResult.results) {
            if (result.status === 'completed' && result.result) {
                moduleReports.push(result.result);
            }
            else {
                // Create a failed report for tasks that couldn't complete
                const moduleId = result.taskId.replace('module:', '');
                const module = modules.find((m) => m.id === moduleId);
                moduleReports.push({
                    id: moduleId,
                    label: module?.label ?? moduleId,
                    scope: module?.scope ?? [],
                    steps: [],
                    status: 'failed',
                });
                if (!continueOnFailure) {
                    // Mark remaining as skipped
                    break;
                }
            }
        }
        return moduleReports;
    }
    /**
     * Process a single module (can be called in parallel or sequentially)
     */
    async processModule(module, mode, modeDefinition, parallelVariants, variantStats, continueOnFailure = true) {
        this.emit({
            type: 'upgrade.module.start',
            timestamp: Date.now(),
            data: { moduleId: module.id, label: module.label, scope: module.scope, mode },
        });
        const moduleReport = {
            id: module.id,
            label: module.label,
            scope: module.scope,
            codemodCommands: module.codemodCommands,
            validationCommands: module.validationCommands,
            steps: [],
            status: 'completed',
        };
        for (const step of module.steps) {
            const outcome = await this.runStep(module, step, mode, parallelVariants);
            moduleReport.steps.push(outcome);
            this.updateVariantStats(variantStats, outcome, modeDefinition);
            if (!outcome.winner.success) {
                moduleReport.status = 'failed';
                // Stop processing this module's steps if continueOnFailure is false
                if (!continueOnFailure) {
                    break;
                }
            }
        }
        this.emit({
            type: 'upgrade.module.complete',
            timestamp: Date.now(),
            data: { moduleId: module.id, status: moduleReport.status },
        });
        return moduleReport;
    }
    async runStep(module, step, mode, parallelVariants = true) {
        const modeDefinition = getModeDefinition(mode);
        this.emit({
            type: 'upgrade.step.start',
            timestamp: Date.now(),
            data: { moduleId: module.id, stepId: step.id, variant: 'primary', mode, parallelVariants },
        });
        const variantResults = {};
        // Determine if we can run variants in parallel
        // Parallel is possible when:
        // 1. parallelVariants is enabled
        // 2. We have both primary and refiner variants
        // 3. We have separate workspace roots for each variant
        const canRunParallel = parallelVariants &&
            modeDefinition.variants.includes('refiner') &&
            this.variantWorkspaceRoots?.refiner &&
            this.variantWorkspaceRoots.refiner !== this.variantWorkspaceRoots.primary;
        if (canRunParallel && modeDefinition.variants.length > 1) {
            // PARALLEL VARIANT EXECUTION
            this.emit({
                type: 'upgrade.step.variants.parallel',
                timestamp: Date.now(),
                data: { moduleId: module.id, stepId: step.id, variants: modeDefinition.variants },
            });
            const variantPromises = modeDefinition.variants.map(async (variant) => {
                const result = await this.safeExecuteVariant({
                    module,
                    step,
                    mode,
                    variant,
                    // In parallel mode, refiner doesn't get primary's result as it runs concurrently
                    previousResult: undefined,
                    workspaceRoot: this.variantWorkspaceRoots?.[variant] ?? this.variantWorkspaceRoots?.primary,
                    repoPolicy: this.repoPolicy,
                });
                return { variant, result };
            });
            const results = await Promise.all(variantPromises);
            for (const { variant, result } of results) {
                variantResults[variant] = result;
            }
        }
        else {
            // SEQUENTIAL VARIANT EXECUTION (original behavior)
            // Refiner gets primary's result for informed refinement
            for (const variant of modeDefinition.variants) {
                const previousResult = variant === 'refiner' ? variantResults.primary : undefined;
                const result = await this.safeExecuteVariant({
                    module,
                    step,
                    mode,
                    variant,
                    previousResult,
                    workspaceRoot: this.variantWorkspaceRoots?.[variant] ?? this.variantWorkspaceRoots?.primary,
                    repoPolicy: this.repoPolicy,
                });
                variantResults[variant] = result;
            }
        }
        const primary = variantResults.primary;
        const refiner = variantResults.refiner;
        const tournamentOutcome = modeDefinition.parallelVariants
            ? this.evaluateTournamentOutcome(module, step, modeDefinition, primary, refiner)
            : null;
        if (tournamentOutcome) {
            this.attachTournamentBreakdown(variantResults, tournamentOutcome);
        }
        const { winner, winnerVariant } = resolveWinnerStrategy({ modeDefinition, variantResults, tournamentOutcome }, (definition, primaryResult, refinerResult) => this.pickWinner(definition, primaryResult, refinerResult));
        this.recordOutcomeArtifacts(module, step, winner, winnerVariant);
        const primaryScore = primary.tournament?.aggregateScore ?? primary.score;
        const refinerScore = refiner?.tournament?.aggregateScore ?? refiner?.score;
        const primaryAccuracy = primary.tournament?.humanAccuracy ?? primary.humanAccuracy;
        const refinerAccuracy = refiner?.tournament?.humanAccuracy ?? refiner?.humanAccuracy;
        this.emit({
            type: 'upgrade.step.complete',
            timestamp: Date.now(),
            data: {
                moduleId: module.id,
                stepId: step.id,
                variant: winnerVariant,
                success: winner.success,
                // Include outcome details for UI scoring
                primaryScore,
                primarySuccess: primary.success,
                refinerScore,
                refinerSuccess: refiner?.success,
                winnerVariant,
                primaryAccuracy,
                refinerAccuracy,
                tournamentRankings: tournamentOutcome?.ranked,
            },
        });
        return {
            stepId: step.id,
            intent: step.intent,
            description: step.description,
            primary,
            refiner,
            winner,
            winnerVariant,
            status: winner.success ? 'completed' : 'failed',
        };
    }
    evaluateTournamentOutcome(module, step, modeDefinition, primary, refiner) {
        // Fast exits: if only one variant succeeded, avoid tournament overhead
        if (primary.success && (!refiner || !refiner.success)) {
            primary.humanAccuracy = 1;
            if (refiner)
                refiner.humanAccuracy = 0;
            this.updateRepoTypeTelemetry(getRepoTypeFromModule(module), 'primary');
            return null;
        }
        if (refiner?.success && !primary.success) {
            refiner.humanAccuracy = 1;
            primary.humanAccuracy = 0;
            this.updateRepoTypeTelemetry(getRepoTypeFromModule(module), 'refiner');
            return null;
        }
        // If neither variant succeeded, skip tournament aggregation for efficiency
        if (!primary.success && (!refiner || !refiner.success)) {
            return null;
        }
        const candidates = [
            this.buildTournamentCandidate('primary', primary, modeDefinition),
        ];
        if (refiner) {
            candidates.push(this.buildTournamentCandidate('refiner', refiner, modeDefinition));
        }
        const task = {
            id: `${module.id}:${step.id}`,
            goal: step.description,
            constraints: module.scope,
            metadata: {
                module: module.label,
                objective: this.objective,
                repoPolicy: this.repoPolicy,
                intent: step.intent,
            },
        };
        const evaluatorConfig = buildEvaluatorConfig(module);
        try {
            return runDualTournament(task, candidates, {
                rewardWeights: evaluatorConfig.rewardWeights,
                evaluators: evaluatorConfig.evaluators,
                maxCandidates: 8,
            });
        }
        catch {
            return null;
        }
    }
    buildTournamentCandidate(variant, result, modeDefinition) {
        const signals = result.rewardSignals ?? {};
        const rawReward = typeof result.score === 'number' ? clampScore(result.score) : 0;
        const adjustedReward = variant === 'refiner' ? clampScore(rawReward + (modeDefinition.refinerBias ?? 0)) : rawReward;
        const evaluatorScores = [
            { evaluatorId: 'reward-signal', score: adjustedReward, weight: 1 },
            { evaluatorId: 'hard-metrics', score: signals.executionSuccess ?? (result.success ? 1 : 0), weight: 1.1 },
        ];
        if (typeof signals.testsPassed === 'number') {
            evaluatorScores.push({ evaluatorId: 'tests', score: signals.testsPassed, weight: 0.8 });
        }
        if (typeof signals.staticAnalysis === 'number') {
            evaluatorScores.push({ evaluatorId: 'static-analysis', score: signals.staticAnalysis, weight: 0.6 });
        }
        if (typeof signals.codeQuality === 'number' || typeof signals.blastRadius === 'number') {
            const qualityScore = averageDefined([
                signals.codeQuality,
                signals.blastRadius,
                signals.staticAnalysis,
            ]);
            if (!Number.isNaN(qualityScore)) {
                evaluatorScores.push({ evaluatorId: 'quality', score: qualityScore, weight: 0.9 });
            }
        }
        return {
            id: variant,
            policyId: variant,
            patchSummary: result.summary,
            metrics: {
                executionSuccess: signals.executionSuccess ?? (result.success ? 1 : 0),
                testsPassed: signals.testsPassed,
                staticAnalysis: signals.staticAnalysis,
                codeQuality: signals.codeQuality,
                blastRadius: signals.blastRadius,
                speedBonus: signals.speedBonus,
            },
            signals: {
                rewardModelScore: adjustedReward,
                selfAssessment: signals.selfAssessment,
            },
            evaluatorScores: evaluatorScores ?? [],
            rawOutput: result.detail,
        };
    }
    attachTournamentBreakdown(variantResults, outcome) {
        for (const entry of outcome.ranked) {
            const variant = entry.candidateId;
            const target = variantResults[variant];
            if (target) {
                target.tournament = entry;
                target.humanAccuracy = entry.humanAccuracy;
            }
        }
        // Update telemetry based on winner to auto-tune per repo type
        const top = outcome.ranked[0];
        if (top) {
            const repoType = getRepoTypeFromOutcomeMetadata(outcome.task);
            this.updateRepoTypeTelemetry(repoType, top.candidateId);
        }
    }
    async safeExecuteVariant(input) {
        try {
            return await this.executor(input);
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            return {
                success: false,
                summary: 'Execution failed',
                detail: message,
                score: 0,
                durationMs: 0,
                execution: {
                    success: false,
                    output: 'Execution failed',
                    duration: 0,
                    command: `${input.module.id}/${input.step.id}/${input.variant}`,
                    error: message,
                },
                notes: ['Executor threw an error'],
            };
        }
    }
    pickWinner(definition, primary, refiner) {
        if (!refiner || !definition.variants.includes('refiner')) {
            return { winner: primary, winnerVariant: 'primary' };
        }
        if (refiner.success && !primary.success) {
            return { winner: refiner, winnerVariant: 'refiner' };
        }
        if (primary.success && !refiner.success) {
            return { winner: primary, winnerVariant: 'primary' };
        }
        const primaryScore = typeof primary.score === 'number' ? primary.score : 0;
        const refinerScore = (typeof refiner.score === 'number' ? refiner.score : 0) + (definition.refinerBias ?? 0);
        const primaryAccuracy = typeof primary.humanAccuracy === 'number' ? primary.humanAccuracy : 0;
        const refinerAccuracy = typeof refiner.humanAccuracy === 'number' ? refiner.humanAccuracy : 0;
        if (refinerScore > primaryScore) {
            return { winner: refiner, winnerVariant: 'refiner' };
        }
        if (primaryScore > refinerScore) {
            return { winner: primary, winnerVariant: 'primary' };
        }
        // Accuracy-aware tie break
        if (refinerAccuracy > primaryAccuracy + 1e-6) {
            return { winner: refiner, winnerVariant: 'refiner' };
        }
        if (primaryAccuracy > refinerAccuracy + 1e-6) {
            return { winner: primary, winnerVariant: 'primary' };
        }
        // Tie breaker: prefer refiner to encourage RL exploration
        return { winner: refiner, winnerVariant: 'refiner' };
    }
    recordOutcomeArtifacts(module, step, result, variant) {
        if (result.findings?.length) {
            this.findings.push(...result.findings);
        }
        const execution = result.execution ?? {
            success: result.success,
            output: result.summary,
            duration: result.durationMs ?? 0,
            command: `${module.id}/${step.id}/${variant}`,
            error: result.success ? undefined : result.detail ?? result.summary,
        };
        // Ensure we only track a single winner record per step to keep reports clean
        this.results.push(execution);
        // Update telemetry after each recorded outcome
        const repoType = getRepoTypeFromModule(module);
        this.updateRepoTypeTelemetry(repoType, variant);
    }
    updateRepoTypeTelemetry(repoType, winner) {
        updateRepoTypeTelemetryMap(repoType, winner);
    }
    updateVariantStats(stats, outcome, modeDefinition) {
        stats.totalSteps += 1;
        if (outcome.winnerVariant === 'refiner') {
            stats.refinerWins += 1;
        }
        else {
            stats.primaryWins += 1;
        }
        if (!outcome.refiner) {
            return;
        }
        const primaryScore = typeof outcome.primary.score === 'number' ? outcome.primary.score : 0;
        const primaryTournamentScore = typeof outcome.primary.tournament?.aggregateScore === 'number'
            ? outcome.primary.tournament.aggregateScore
            : primaryScore;
        const refinerScoreRaw = typeof outcome.refiner.tournament?.aggregateScore === 'number'
            ? outcome.refiner.tournament.aggregateScore
            : typeof outcome.refiner.score === 'number'
                ? outcome.refiner.score
                : 0;
        const refinerScore = refinerScoreRaw + (modeDefinition.refinerBias ?? 0);
        const scoresClose = Math.abs(primaryTournamentScore - refinerScore) < 1e-6;
        const bothSucceeded = outcome.primary.success && outcome.refiner.success;
        if (bothSucceeded && scoresClose) {
            stats.ties += 1;
        }
    }
}
export function clampScore(value, min = 0, max = 1) {
    if (!Number.isFinite(value))
        return min;
    return Math.max(min, Math.min(max, value));
}
function averageDefined(values) {
    const nums = values.filter((v) => typeof v === 'number');
    if (!nums.length)
        return NaN;
    const total = nums.reduce((sum, val) => sum + val, 0);
    return total / nums.length;
}
function buildEvaluatorConfig(module) {
    const repoType = getRepoTypeFromModule(module);
    // Default: balanced weights
    let rewardWeights = DEFAULT_HUMAN_REWARD_WEIGHTS;
    let hardWeight = 1.35;
    let qualityWeight = 1.0;
    let rewardWeight = 0.9;
    if (repoType === 'tests') {
        rewardWeights = { alpha: 0.7, beta: 0.15, gamma: 0.15 };
        hardWeight = 1.55;
        qualityWeight = 0.9;
        rewardWeight = 0.85;
    }
    else if (repoType === 'docs') {
        rewardWeights = { alpha: 0.45, beta: 0.3, gamma: 0.25 };
        hardWeight = 1.0;
        qualityWeight = 1.2;
        rewardWeight = 1.0;
    }
    else if (repoType === 'refactor') {
        rewardWeights = { alpha: 0.55, beta: 0.25, gamma: 0.2 };
        hardWeight = 1.25;
        qualityWeight = 1.15;
        rewardWeight = 0.95;
    }
    // Telemetry-based nudges per repo type
    const telemetry = RepoUpgradeOrchestrator.repoTypeTelemetry.get(repoType);
    if (telemetry) {
        const total = Math.max(1, telemetry.winsPrimary + telemetry.winsRefiner);
        const bias = (telemetry.winsPrimary - telemetry.winsRefiner) / total;
        if (bias > 0.1) {
            hardWeight = clampScore(hardWeight + 0.1, 0.8, 2);
            rewardWeights = {
                alpha: clampScore((rewardWeights.alpha ?? 0.6) + 0.05, 0, 1),
                beta: rewardWeights.beta,
                gamma: clampScore((rewardWeights.gamma ?? 0.2) - 0.02, 0, 1),
            };
        }
        else if (bias < -0.1) {
            qualityWeight = clampScore(qualityWeight + 0.1, 0.8, 2);
            rewardWeight = clampScore(rewardWeight + 0.05, 0.5, 1.5);
            rewardWeights = {
                alpha: clampScore((rewardWeights.alpha ?? 0.6) - 0.05, 0, 1),
                beta: clampScore((rewardWeights.beta ?? 0.25) + 0.05, 0, 1),
                gamma: clampScore((rewardWeights.gamma ?? 0.2) + 0.02, 0, 1),
            };
        }
    }
    return {
        rewardWeights,
        evaluators: [
            { id: 'hard-metrics', label: 'Hard metrics', weight: hardWeight, kind: 'hard' },
            { id: 'quality', label: 'Quality', weight: qualityWeight, kind: 'hybrid' },
            { id: 'reward-signal', label: 'Reward signal', weight: rewardWeight, kind: 'hybrid' },
        ],
    };
}
function getRepoTypeFromModule(module) {
    const scope = (module.scope || []).join(' ').toLowerCase();
    const label = (module.label || '').toLowerCase();
    if (/\btest|__tests__|spec\b/i.test(scope) || /\btest\b/i.test(label))
        return 'tests';
    if (/\bdocs?\b/i.test(scope) || /\bdoc\b/i.test(label))
        return 'docs';
    if (/\brefactor|cleanup|source\b/i.test(scope + ' ' + label))
        return 'refactor';
    return 'general';
}
function getRepoTypeFromOutcomeMetadata(task) {
    const scopeText = Array.isArray(task.constraints) ? task.constraints.join(' ').toLowerCase() : '';
    const intent = typeof task.metadata?.intent === 'string' ? task.metadata.intent.toLowerCase() : '';
    if (/\btest|__tests__|spec\b/i.test(scopeText) || /\btest\b/i.test(intent))
        return 'tests';
    if (/\bdoc\b/i.test(scopeText) || /\bdoc\b/i.test(intent))
        return 'docs';
    if (/\brefactor|cleanup\b/i.test(scopeText + ' ' + intent))
        return 'refactor';
    return 'general';
}
function updateRepoTypeTelemetryMap(repoType, winner) {
    if (RepoUpgradeOrchestrator.disableTelemetryPersistence) {
        return;
    }
    ensureTelemetryLoaded();
    const current = RepoUpgradeOrchestrator.repoTypeTelemetry.get(repoType) ?? { winsPrimary: 0, winsRefiner: 0 };
    const updated = winner === 'primary'
        ? { ...current, winsPrimary: current.winsPrimary + 1 }
        : { ...current, winsRefiner: current.winsRefiner + 1 };
    RepoUpgradeOrchestrator.repoTypeTelemetry.set(repoType, updated);
    persistTelemetry();
}
// Simple persistence of repo-type telemetry to inform future runs
const TELEMETRY_DIR = join(homedir(), '.agi');
const TELEMETRY_FILE = join(TELEMETRY_DIR, 'rl-telemetry.json');
let telemetryLoaded = false;
function ensureTelemetryLoaded() {
    if (RepoUpgradeOrchestrator.disableTelemetryPersistence)
        return;
    if (telemetryLoaded)
        return;
    telemetryLoaded = true;
    try {
        if (!existsSync(TELEMETRY_FILE))
            return;
        const data = JSON.parse(readFileSync(TELEMETRY_FILE, 'utf-8'));
        if (data && typeof data === 'object') {
            for (const [key, value] of Object.entries(data)) {
                if (value && typeof value === 'object' && 'winsPrimary' in value && 'winsRefiner' in value) {
                    RepoUpgradeOrchestrator.repoTypeTelemetry.set(key, {
                        winsPrimary: Number(value.winsPrimary) || 0,
                        winsRefiner: Number(value.winsRefiner) || 0,
                    });
                }
            }
        }
    }
    catch {
        // ignore load errors
    }
}
function persistTelemetry() {
    if (RepoUpgradeOrchestrator.disableTelemetryPersistence)
        return;
    try {
        mkdirSync(TELEMETRY_DIR, { recursive: true });
        const payload = {};
        for (const [key, value] of RepoUpgradeOrchestrator.repoTypeTelemetry.entries()) {
            payload[key] = value;
        }
        writeFileSync(TELEMETRY_FILE, JSON.stringify(payload, null, 2), 'utf-8');
    }
    catch {
        // ignore persistence errors
    }
}
function buildDefaultSteps(moduleId) {
    return [
        {
            id: `${moduleId}-analyze`,
            intent: 'analyze',
            description: `Map the upgrade surface for ${moduleId}. Identify risky files and coupling.`,
            prompt: 'Inventory files, dependencies, and breaking-change areas before upgrading.',
        },
        {
            id: `${moduleId}-upgrade`,
            intent: 'upgrade',
            description: `Apply modular upgrades for ${moduleId} with minimal blast radius.`,
            prompt: 'Execute codemods or targeted edits. Keep edits small, atomic, and well scoped.',
        },
        {
            id: `${moduleId}-verify`,
            intent: 'verify',
            description: `Validate ${moduleId} after the upgrade.`,
            prompt: 'Run tests/lint/health checks relevant to this scope. Summarize residual risks.',
        },
    ];
}
function getModeDefinition(mode) {
    // AlphaZero dual-agent tournament is now the default mode
    return REPO_UPGRADE_MODE_DEFINITIONS[mode] ?? REPO_UPGRADE_MODE_DEFINITIONS['dual-rl-tournament'];
}
//# sourceMappingURL=repoUpgradeOrchestrator.js.map