"use strict";
/**
 * Typed accessor for chorus's concurrency settings — caps on parallel
 * CLI subprocesses, daemon-wide.
 *
 * Two knobs:
 *
 *   - `maxParallelCli` (1..10, default 3): GLOBAL cap on the total
 *     number of local-CLI shim subprocesses (reviewers + doer combined)
 *     in flight across the whole daemon. HTTP-dispatched shims
 *     (openrouter and friends) don't count — they're network calls and
 *     consume zero local CPU/RAM.
 *
 *   - `perCli` (1..5, defaults below): per-binary cap, also daemon-wide.
 *     Lets the user say "max 2 opencode" even when 4+ chats are in
 *     flight — opencode subprocesses are ~450 MB each and a 4-stack hits
 *     swap. Composes with the global cap as `min(global, perCli)`: a
 *     reviewer slot must acquire BOTH a global slot AND a per-CLI slot
 *     before spawning, whichever is tighter is the queue.
 *
 * Why a single setting object instead of two: keeps the YAML/Form view
 * tidy, and lets `mm.update_settings` apply both atomically. Empty
 * `perCli` values fall through to defaults so the user only stores
 * deltas from default.
 *
 * The daemon reads this dynamically each acquire (not per chat or per
 * boot) so settings changes take effect on the next reviewer to start —
 * no daemon restart needed.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports._defaults = exports.ConcurrencySchema = exports.CLI_LINEAGES = void 0;
exports.resolvePerCliCap = resolvePerCliCap;
exports.getConcurrency = getConcurrency;
exports.setConcurrency = setConcurrency;
const zod_1 = require("zod");
const db_1 = require("../db");
/** CLIs we cap individually. Mirrors the keys in `cli-detect.ts`. */
exports.CLI_LINEAGES = [
    'claude-code',
    'codex-cli',
    'gemini-cli',
    'opencode-cli',
    'kimi-cli',
];
/**
 * Defaults reflect what we learned during the May 2026 OOM incident:
 * opencode is heaviest (~450 MB / proc), gemini parser leaks listeners
 * under churn so we keep it tight, claude/codex are lighter and can run
 * 3-wide. Adjustable in /settings.
 */
const DEFAULT_PER_CLI = {
    'claude-code': 3,
    'codex-cli': 3,
    'gemini-cli': 2,
    'opencode-cli': 2,
    'kimi-cli': 2,
};
const DEFAULT_MAX_PARALLEL_CLI = 3;
const PER_CLI_KEY_SCHEMA = zod_1.z.enum(exports.CLI_LINEAGES);
exports.ConcurrencySchema = zod_1.z.object({
    maxParallelCli: zod_1.z.number().int().min(1).max(10).default(DEFAULT_MAX_PARALLEL_CLI),
    perCli: zod_1.z
        .record(PER_CLI_KEY_SCHEMA, zod_1.z.number().int().min(1).max(5))
        .default({}),
});
const SETTINGS_KEY = 'concurrency';
/**
 * Resolve the per-CLI cap for a given lineage with default fallback.
 * Centralized so callers don't sprinkle `?? DEFAULT_PER_CLI[k]` checks.
 */
function resolvePerCliCap(config, lineage) {
    return config.perCli[lineage] ?? DEFAULT_PER_CLI[lineage];
}
async function getConcurrency() {
    const raw = await db_1.settings.get(SETTINGS_KEY);
    if (raw === null) {
        return exports.ConcurrencySchema.parse({});
    }
    // safeParse so a hand-edited bogus value never crashes the runner —
    // fall back to defaults, the cockpit will surface the broken state on
    // next save anyway.
    const result = exports.ConcurrencySchema.safeParse(raw);
    if (!result.success) {
        return exports.ConcurrencySchema.parse({});
    }
    return result.data;
}
async function setConcurrency(config) {
    const validated = exports.ConcurrencySchema.parse(config);
    await db_1.settings.set(SETTINGS_KEY, validated);
}
/**
 * Defaults exposed for the cockpit so the form can pre-populate
 * placeholder values matching what the daemon would use if a row is
 * left blank.
 */
exports._defaults = {
    maxParallelCli: DEFAULT_MAX_PARALLEL_CLI,
    perCli: DEFAULT_PER_CLI,
};
//# sourceMappingURL=concurrency.js.map