"use strict";
// src/orchestrator/router/ModelTierRouter.ts
// v1.14.0 — Pick a (provider, model) for a given task tier, honoring
// OPENLIFE_INFERENCE_MODE and OPENLIFE_PROVIDER_PREFERENCE.
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 });
exports.ModelTierRouter = void 0;
exports.readRouterConfig = readRouterConfig;
exports.writeRouterConfig = writeRouterConfig;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const ProviderCapabilityCatalog_1 = require("./ProviderCapabilityCatalog");
const ProviderProbe_1 = require("./ProviderProbe");
const TASK_TIER = {
    'chat.fast': 'smart',
    'chat.deep': 'deep',
    'composer.summarize': 'fast',
    'composer.rerank': 'fast',
    'composer.expand': 'fast',
    'composer.story-match': 'fast',
    'embed': 'embed',
};
function routerConfigPath() {
    const home = process.env.HOME || process.env.USERPROFILE || '/tmp';
    return path.join(home, '.openlife', 'router.json');
}
function readRouterConfig() {
    try {
        const raw = fs.readFileSync(routerConfigPath(), 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return {};
    }
}
function writeRouterConfig(cfg) {
    const p = routerConfigPath();
    fs.mkdirSync(path.dirname(p), { recursive: true });
    fs.writeFileSync(p, JSON.stringify(cfg, null, 2));
}
function currentMode(cfg) {
    const env = (process.env.OPENLIFE_INFERENCE_MODE || '').toLowerCase().trim();
    if (env === 'cli-only' || env === 'hybrid' || env === 'api-only' || env === 'local-only')
        return env;
    return cfg.mode || 'cli-only';
}
function preferenceList(cfg) {
    const env = (process.env.OPENLIFE_PROVIDER_PREFERENCE || '').trim();
    if (env)
        return env.split(',').map(s => s.trim()).filter(Boolean);
    if (cfg.preference && cfg.preference.length > 0)
        return cfg.preference;
    return ['anthropic', 'gemini-cli', 'openai-cli', 'ollama'];
}
function providerAllowedInMode(p, mode) {
    if (mode === 'cli-only')
        return p.cliAvailable && p.provider !== 'openai-api' && p.provider !== 'gemini-api';
    if (mode === 'api-only')
        return p.apiAvailable;
    if (mode === 'local-only')
        return p.provider === 'ollama' && p.cliAvailable;
    // hybrid
    return p.cliAvailable || p.apiAvailable;
}
class ModelTierRouter {
    static pick(task) {
        const cfg = readRouterConfig();
        const mode = currentMode(cfg);
        const pref = preferenceList(cfg);
        const probed = ProviderProbe_1.ProviderProbe.run();
        const tier = TASK_TIER[task];
        for (const providerName of pref) {
            const probe = probed.find(p => p.provider === providerName);
            if (!probe || !providerAllowedInMode(probe, mode))
                continue;
            // Explicit overrides take precedence over tier roster.
            let modelId = null;
            if (tier === 'fast' && cfg.fastModel?.[providerName])
                modelId = cfg.fastModel[providerName];
            if (tier === 'smart' && cfg.smartModel?.[providerName])
                modelId = cfg.smartModel[providerName];
            if (!modelId) {
                const m = (0, ProviderCapabilityCatalog_1.pickModel)(probe, tier);
                if (m)
                    modelId = m.id;
            }
            if (modelId) {
                return { task, tier, provider: providerName, model: modelId, mode };
            }
        }
        return null;
    }
    static toModelIdentifier(d) {
        return { provider: d.provider, name: d.model, raw: `${d.provider}/${d.model}` };
    }
    /**
     * First-run autoconfig hook used by `openlife ask` / `bridge push chat`:
     * if no router.json exists, write a sensible default based on probing.
     * Returns true if it actually wrote a file (i.e. first run).
     */
    static ensureConfigured() {
        if (fs.existsSync(routerConfigPath()))
            return false;
        // Respect the on-disk probe cache when present (test stubs rely on this,
        // and warm-cache reuse cuts first-run startup). ProviderProbe.run() without
        // force runs a fresh probe only if no cache exists yet.
        const probed = ProviderProbe_1.ProviderProbe.run();
        const available = probed.filter(p => p.cliAvailable);
        const order = ['anthropic', 'gemini-cli', 'openai-cli', 'ollama'];
        const sorted = order.filter(o => available.find(a => a.provider === o));
        const cfg = { mode: 'cli-only', preference: sorted.length > 0 ? sorted : order };
        writeRouterConfig(cfg);
        const top = sorted[0];
        if (top) {
            const probe = probed.find(p => p.provider === top);
            const fast = (0, ProviderCapabilityCatalog_1.pickModel)(probe, 'fast');
            const smart = (0, ProviderCapabilityCatalog_1.pickModel)(probe, 'smart');
            const fastLabel = fast ? fast.id : '<auto>';
            const smartLabel = smart ? smart.id : '<auto>';
            console.log(`[OPENLIFE] Detected: ${probe.cliBinary} (${probe.models.map(m => m.id).join(', ')}) ✓`);
            console.log(`[OPENLIFE] Using ${top} (${smartLabel} for answers, ${fastLabel} for retrieval).`);
            console.log(`[OPENLIFE] Change anytime with: openlife provider setup`);
        }
        else {
            console.log(`[OPENLIFE] No CLI provider detected (claude / gemini / codex / ollama). Run \`openlife provider setup\` after installing one.`);
        }
        return true;
    }
}
exports.ModelTierRouter = ModelTierRouter;
