"use strict";
// src/test_model_tier_router.ts
// v1.14.0 — Router/Probe/Catalog regression. We stub fs + child_process
// by writing a custom router.json into a tmp HOME so we can drive
// preference/mode without depending on the real installed CLIs.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const ProviderCapabilityCatalog_1 = require("./orchestrator/router/ProviderCapabilityCatalog");
const ModelTierRouter_1 = require("./orchestrator/router/ModelTierRouter");
function assert(cond, msg) {
    if (!cond)
        throw new Error(`assertion failed: ${msg}`);
}
function withTmpHome(fn) {
    const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'router-test-'));
    const orig = process.env.HOME;
    process.env.HOME = tmp;
    try {
        return fn(tmp);
    }
    finally {
        process.env.HOME = orig;
        try {
            fs.rmSync(tmp, { recursive: true, force: true });
        }
        catch { /* ignore */ }
    }
}
function withEnv(vars, fn) {
    const saved = {};
    for (const k of Object.keys(vars)) {
        saved[k] = process.env[k];
        if (vars[k] === undefined)
            delete process.env[k];
        else
            process.env[k] = vars[k];
    }
    try {
        return fn();
    }
    finally {
        for (const k of Object.keys(saved)) {
            if (saved[k] === undefined)
                delete process.env[k];
            else
                process.env[k] = saved[k];
        }
    }
}
function fakeProbe(home, available) {
    const entries = ProviderCapabilityCatalog_1.DEFAULT_CATALOG.map(e => ({ ...e, cliAvailable: available.includes(e.provider), apiAvailable: false }));
    const file = path.join(home, '.openlife', 'provider-probe.json');
    fs.mkdirSync(path.dirname(file), { recursive: true });
    fs.writeFileSync(file, JSON.stringify({ ts: Date.now(), entries }, null, 2));
}
function main() {
    // 1. catalog has all expected providers
    for (const need of ['openai-cli', 'gemini-cli', 'anthropic', 'ollama']) {
        assert((0, ProviderCapabilityCatalog_1.findProvider)(need), `missing provider in catalog: ${need}`);
    }
    console.log(`✓ scenario 1: catalog covers claude/gemini/codex/ollama`);
    // 2. pickModel returns tier-appropriate model
    const claude = (0, ProviderCapabilityCatalog_1.findProvider)('anthropic');
    const fast = (0, ProviderCapabilityCatalog_1.pickModel)(claude, 'fast');
    const smart = (0, ProviderCapabilityCatalog_1.pickModel)(claude, 'smart');
    const deep = (0, ProviderCapabilityCatalog_1.pickModel)(claude, 'deep');
    assert(fast && fast.id === 'haiku', `fast=haiku, got ${fast?.id}`);
    assert(smart && smart.id === 'sonnet', `smart=sonnet, got ${smart?.id}`);
    assert(deep && deep.id === 'opus', `deep=opus, got ${deep?.id}`);
    console.log(`✓ scenario 2: pickModel returns tier-appropriate Claude model`);
    // 3. cli-only mode picks the first available CLI in preference order
    withTmpHome((home) => {
        fakeProbe(home, ['anthropic', 'gemini-cli']);
        (0, ModelTierRouter_1.writeRouterConfig)({ mode: 'cli-only', preference: ['anthropic', 'gemini-cli', 'openai-cli', 'ollama'] });
        const d = ModelTierRouter_1.ModelTierRouter.pick('chat.fast');
        assert(d, 'should find a decision');
        assert(d.provider === 'anthropic', `expected anthropic primary, got ${d.provider}`);
        assert(d.model === 'sonnet', `chat.fast → smart tier (sonnet), got ${d.model}`);
        assert(d.mode === 'cli-only', `mode = cli-only, got ${d.mode}`);
    });
    console.log(`✓ scenario 3: cli-only honors preference + tier`);
    // 4. preference override skips the first available
    withTmpHome((home) => {
        fakeProbe(home, ['anthropic', 'gemini-cli']);
        (0, ModelTierRouter_1.writeRouterConfig)({ mode: 'cli-only', preference: ['gemini-cli', 'anthropic'] });
        const d = ModelTierRouter_1.ModelTierRouter.pick('chat.fast');
        assert(d && d.provider === 'gemini-cli', `expected gemini-cli, got ${d?.provider}`);
    });
    console.log(`✓ scenario 4: preference list controls fallback order`);
    // 5. no CLI available + cli-only → null (caller must remediate)
    withTmpHome((home) => {
        fakeProbe(home, []);
        (0, ModelTierRouter_1.writeRouterConfig)({ mode: 'cli-only' });
        const d = ModelTierRouter_1.ModelTierRouter.pick('chat.fast');
        assert(d === null, `cli-only with nothing available returns null`);
    });
    console.log(`✓ scenario 5: cli-only with no CLI installed → null (no silent API fallback)`);
    // 6. hybrid falls back to ollama when no other CLI is around
    withTmpHome((home) => {
        fakeProbe(home, ['ollama']);
        (0, ModelTierRouter_1.writeRouterConfig)({ mode: 'hybrid', preference: ['anthropic', 'gemini-cli', 'openai-cli', 'ollama'] });
        const d = ModelTierRouter_1.ModelTierRouter.pick('chat.fast');
        assert(d && d.provider === 'ollama', `hybrid w/ only ollama → ollama, got ${d?.provider}`);
    });
    console.log(`✓ scenario 6: hybrid mode falls back to ollama when no priority CLIs exist`);
    // 7. env override beats router.json
    withTmpHome((home) => {
        fakeProbe(home, ['anthropic', 'gemini-cli']);
        (0, ModelTierRouter_1.writeRouterConfig)({ mode: 'cli-only', preference: ['anthropic'] });
        withEnv({ OPENLIFE_PROVIDER_PREFERENCE: 'gemini-cli,anthropic' }, () => {
            const d = ModelTierRouter_1.ModelTierRouter.pick('chat.fast');
            assert(d && d.provider === 'gemini-cli', `env override → gemini-cli, got ${d?.provider}`);
        });
    });
    console.log(`✓ scenario 7: OPENLIFE_PROVIDER_PREFERENCE env wins over router.json`);
    // 8. ensureConfigured writes router.json on first call only
    withTmpHome((home) => {
        fakeProbe(home, ['anthropic']);
        const first = ModelTierRouter_1.ModelTierRouter.ensureConfigured();
        const second = ModelTierRouter_1.ModelTierRouter.ensureConfigured();
        assert(first === true, 'first call writes config');
        assert(second === false, 'second call is a no-op');
        const cfg = (0, ModelTierRouter_1.readRouterConfig)();
        assert(cfg.mode === 'cli-only', `default mode cli-only, got ${cfg.mode}`);
        assert(cfg.preference?.[0] === 'anthropic', `first preference = anthropic, got ${cfg.preference?.[0]}`);
    });
    console.log(`✓ scenario 8: ensureConfigured writes default router.json exactly once`);
    console.log('TEST_MODEL_TIER_ROUTER_OK');
}
try {
    main();
}
catch (err) {
    console.error(`TEST_MODEL_TIER_ROUTER_FAIL: ${err instanceof Error ? err.message : String(err)}`);
    process.exit(1);
}
