// engine/system-resources/spawn-policy.ts — SPEC-705: RAM-aware parallelism policy.
// Decides how many agents can be spawned in parallel given current host RAM.
import { getHostMemorySnapshot } from './ram-checker.js';
// ---------------------------------------------------------------------------
// Constants (exported)
// ---------------------------------------------------------------------------
export const DEFAULT_AGENT_MB = 800;
export const DEFAULT_CRITICAL_GB = 4;
export const DEFAULT_MODERATE_GB = 8;
export const SAFE_FALLBACK_PARALLELISM = 2;
export const SNAPSHOT_CACHE_TTL_MS = 10_000;
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function calcAllowed(freeGB, agentMB, requested) {
    const agentGB = agentMB / 1024;
    const available = freeGB - 2; // 2GB headroom
    if (available <= 0) {
        return 1;
    }
    return Math.max(1, Math.min(Math.floor(available / agentGB), requested));
}
function extractGuardrailConfig(conventions) {
    if (!conventions) {
        return {};
    }
    const raw = conventions.ramGuardrail;
    if (!raw || typeof raw !== 'object') {
        return {};
    }
    return raw;
}
function applyEnvCap(requested) {
    const envCap = process.env.PLANU_MAX_PARALLEL_AGENTS;
    if (envCap === undefined) {
        return null;
    }
    const cap = parseInt(envCap, 10);
    const allowed = Number.isFinite(cap) && cap > 0 ? Math.min(cap, requested) : requested;
    return { allowed, requested, reason: 'env-cap', freeGBObserved: null, pressureLevel: 'unknown' };
}
function applySnapshot(snapshot, requested, agentMB, criticalGB, moderateGB) {
    if (snapshot.pressureLevel === 'unknown' || snapshot.freeGB === null) {
        const allowed = Math.min(requested, SAFE_FALLBACK_PARALLELISM);
        if (allowed < requested) {
            console.warn(`RAM-aware: ${requested} agents requested, ${allowed} allowed — probe failed (safe default)`);
        }
        return {
            allowed,
            requested,
            reason: 'probe-failed-safe-default',
            freeGBObserved: null,
            pressureLevel: 'unknown',
        };
    }
    const { freeGB, pressureLevel } = snapshot;
    if (pressureLevel === 'critical' || freeGB < criticalGB) {
        console.warn(`RAM-aware: ${requested} agents requested, 1 allowed — host has ${freeGB.toFixed(1)}GB free, threshold ${criticalGB}GB (pressure: ${pressureLevel})`);
        return { allowed: 1, requested, reason: 'critical-ram', freeGBObserved: freeGB, pressureLevel };
    }
    if (freeGB < moderateGB) {
        const allowed = calcAllowed(freeGB, agentMB, requested);
        if (allowed < requested) {
            console.warn(`RAM-aware: ${requested} agents requested, ${allowed} allowed — host has ${freeGB.toFixed(1)}GB free, threshold ${moderateGB}GB (pressure: ${pressureLevel})`);
        }
        return {
            allowed,
            requested,
            reason: allowed < requested ? 'moderate-ram' : 'ok',
            freeGBObserved: freeGB,
            pressureLevel,
        };
    }
    return { allowed: requested, requested, reason: 'ok', freeGBObserved: freeGB, pressureLevel };
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Decide how many parallel agents are safe to spawn on this host.
 * Priority: PLANU_MAX_PARALLEL_AGENTS env > conventions.ramGuardrail.enabled=false > RAM probe.
 */
export async function decideAgentParallelism(opts) {
    const { requested, agentSizeMB = DEFAULT_AGENT_MB } = opts;
    const guardrailConfig = extractGuardrailConfig(opts.conventions);
    const envDecision = applyEnvCap(requested);
    if (envDecision) {
        return envDecision;
    }
    if (guardrailConfig.enabled === false) {
        return {
            allowed: requested,
            requested,
            reason: 'override-disabled',
            freeGBObserved: null,
            pressureLevel: 'unknown',
        };
    }
    const snapshot = await getHostMemorySnapshot();
    return applySnapshot(snapshot, requested, guardrailConfig.agentMB ?? agentSizeMB, guardrailConfig.criticalGB ?? DEFAULT_CRITICAL_GB, guardrailConfig.moderateGB ?? DEFAULT_MODERATE_GB);
}
//# sourceMappingURL=spawn-policy.js.map