"use strict";
// Config profiles — named presets that partially populate a LoopConfig.
//
// A profile is a named object with overrides for common LoopConfig fields,
// permission mode, and tool filtering. Callers apply a profile on top of
// their base config with `applyProfile()`, which shallow-merges the overrides.
//
// Built-in profiles cover the most common run modes:
//   - code_patch     — edit-focused, verification on, read-before-write enforced
//   - safe_readonly  — no writes, no shell, pure research / Q&A
//   - local_only     — restrict to Ollama models, no cloud fallback
//   - research       — web search + file read, no edits
//   - fast_draft     — short turn budget, no verification, warn-mode gate
//   - full_auto      — high autonomy, auto-continue, large turn budget
//
// Custom profiles can be defined in <projectDir>/.harness/profiles.json.
// Each entry follows the same shape and is merged over a built-in profile
// with the same name (if any), so users can extend built-ins.
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.BUILTIN_PROFILES = void 0;
exports.applyProfile = applyProfile;
exports.getProfile = getProfile;
exports.listProfiles = listProfiles;
exports.loadCustomProfiles = loadCustomProfiles;
exports.saveCustomProfiles = saveCustomProfiles;
exports.filterToolsByProfile = filterToolsByProfile;
exports.auditCustomProfile = auditCustomProfile;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
// ─── Built-in profiles ──────────────────────────────────────────────
const CODE_PATCH = {
    name: 'code_patch',
    description: 'Edit-focused mode with verification and read-before-write enforcement.',
    maxTurns: 15,
    readBeforeWrite: { mode: 'enforce', allowNewFiles: true },
    verify: { enabled: true },
    autoContinue: true,
    autoContinueLimit: 5,
    outputValidation: { enabled: true, profile: 'coding-answer' },
    permissionMode: 'acceptEdits',
};
const SAFE_READONLY = {
    name: 'safe_readonly',
    description: 'No writes, no shell — pure research and Q&A.',
    maxTurns: 10,
    readBeforeWrite: { mode: 'off' },
    verify: { enabled: false },
    autoContinue: false,
    permissionMode: 'default',
    blockedTools: [
        'file_write', 'file_edit', 'file_move', 'file_delete',
        'bash', 'make_directory', 'remember',
    ],
};
const LOCAL_ONLY = {
    name: 'local_only',
    description: 'Restrict to local Ollama models — no cloud API calls.',
    localOnly: true,
};
const RESEARCH = {
    name: 'research',
    description: 'Web search + file reading, no file edits.',
    maxTurns: 12,
    readBeforeWrite: { mode: 'off' },
    verify: { enabled: false },
    autoContinue: true,
    autoContinueLimit: 3,
    permissionMode: 'default',
    blockedTools: [
        'file_write', 'file_edit', 'file_move', 'file_delete',
        'make_directory',
    ],
};
const FAST_DRAFT = {
    name: 'fast_draft',
    description: 'Quick iteration — short budget, no verification, warn-only gate.',
    maxTurns: 6,
    maxTimeMs: 2 * 60 * 1000,
    readBeforeWrite: { mode: 'warn', allowNewFiles: true },
    verify: { enabled: false },
    autoContinue: true,
    autoContinueLimit: 2,
    permissionMode: 'acceptEdits',
};
const FULL_AUTO = {
    name: 'full_auto',
    description: 'High autonomy — large turn budget, auto-continue, verification enabled.',
    maxTurns: 30,
    maxTimeMs: 10 * 60 * 1000,
    readBeforeWrite: { mode: 'enforce', allowNewFiles: true },
    verify: { enabled: true },
    autoContinue: true,
    autoContinueLimit: 10,
    permissionMode: 'dontAsk',
    outputValidation: { enabled: true },
};
exports.BUILTIN_PROFILES = [
    CODE_PATCH,
    SAFE_READONLY,
    LOCAL_ONLY,
    RESEARCH,
    FAST_DRAFT,
    FULL_AUTO,
];
// ─── Apply ──────────────────────────────────────────────────────────
/**
 * Apply a profile's overrides onto a base `LoopConfig`.
 * Returns a new config; does not mutate the input.
 *
 * Only fields explicitly set on the profile are applied; `undefined`
 * fields are skipped so the base config's values are preserved.
 */
function applyProfile(base, profile) {
    const result = { ...base };
    if (profile.maxTurns !== undefined)
        result.maxTurns = profile.maxTurns;
    if (profile.maxTimeMs !== undefined)
        result.maxTimeMs = profile.maxTimeMs;
    if (profile.unproductiveTurnLimit !== undefined)
        result.unproductiveTurnLimit = profile.unproductiveTurnLimit;
    if (profile.repeatedToolFailureLimit !== undefined)
        result.repeatedToolFailureLimit = profile.repeatedToolFailureLimit;
    if (profile.readBeforeWrite !== undefined)
        result.readBeforeWrite = profile.readBeforeWrite;
    if (profile.verify !== undefined)
        result.verify = profile.verify;
    if (profile.autoContinue !== undefined)
        result.autoContinue = profile.autoContinue;
    if (profile.autoContinueLimit !== undefined)
        result.autoContinueLimit = profile.autoContinueLimit;
    if (profile.costTracking !== undefined) {
        result.costTracking = { ...result.costTracking, ...profile.costTracking };
    }
    if (profile.outputValidation !== undefined) {
        result.outputValidation = {
            ...result.outputValidation,
            ...profile.outputValidation,
        };
    }
    return result;
}
// ─── Lookup ─────────────────────────────────────────────────────────
/**
 * Get a profile by name. Checks custom profiles first, then built-ins.
 * Returns `undefined` if no profile with that name exists.
 */
async function getProfile(name, projectDir) {
    if (projectDir) {
        const custom = await loadCustomProfiles(projectDir);
        const found = custom.find((p) => p.name === name);
        if (found) {
            // Merge custom on top of built-in with the same name (if any)
            const builtin = exports.BUILTIN_PROFILES.find((p) => p.name === name);
            return builtin ? mergeProfiles(builtin, found) : found;
        }
    }
    return exports.BUILTIN_PROFILES.find((p) => p.name === name);
}
/**
 * List all available profiles (built-in + custom from project dir).
 * Custom profiles with the same name as a built-in shadow the built-in.
 */
async function listProfiles(projectDir) {
    const custom = projectDir ? await loadCustomProfiles(projectDir) : [];
    const customNames = new Set(custom.map((p) => p.name));
    const builtins = exports.BUILTIN_PROFILES.filter((p) => !customNames.has(p.name));
    return [...builtins, ...custom];
}
// ─── Persistence ────────────────────────────────────────────────────
const PROFILES_FILENAME = 'profiles.json';
/**
 * Load custom profiles from `<projectDir>/.harness/profiles.json`.
 * Returns [] if the file is absent or corrupt.
 */
async function loadCustomProfiles(projectDir) {
    const filePath = path.join(projectDir, '.harness', PROFILES_FILENAME);
    try {
        const raw = await fs.readFile(filePath, 'utf8');
        const parsed = JSON.parse(raw);
        if (!Array.isArray(parsed))
            return [];
        const profiles = parsed.filter(isValidProfile);
        // Log warnings for dangerous profiles
        for (const profile of profiles) {
            const warnings = auditCustomProfile(profile);
            for (const warning of warnings) {
                console.warn(`[configProfiles] ⚠️  ${warning}`);
            }
        }
        return profiles;
    }
    catch {
        return [];
    }
}
/**
 * Save custom profiles to `<projectDir>/.harness/profiles.json`.
 */
async function saveCustomProfiles(projectDir, profiles) {
    const dir = path.join(projectDir, '.harness');
    await fs.mkdir(dir, { recursive: true });
    await fs.writeFile(path.join(dir, PROFILES_FILENAME), JSON.stringify(profiles, null, 2), 'utf8');
}
/**
 * Filter a tool pool according to a profile's `allowedTools` / `blockedTools`.
 * Returns a new array; does not mutate the input.
 */
function filterToolsByProfile(tools, profile) {
    let filtered = tools;
    if (profile.allowedTools && profile.allowedTools.length > 0) {
        const allowed = new Set(profile.allowedTools);
        filtered = filtered.filter((t) => allowed.has(t.name));
    }
    if (profile.blockedTools && profile.blockedTools.length > 0) {
        const blocked = new Set(profile.blockedTools);
        filtered = filtered.filter((t) => !blocked.has(t.name));
    }
    return filtered;
}
// ─── Helpers ─────────────────────────────────────────────────────────
function mergeProfiles(base, override) {
    return {
        ...base,
        ...override,
        // Preserve description from override if provided, otherwise base
        description: override.description || base.description,
    };
}
function isValidProfile(obj) {
    return (typeof obj === 'object' &&
        obj !== null &&
        typeof obj.name === 'string' &&
        typeof obj.description === 'string');
}
const DANGEROUS_TOOLS = new Set(['bash', 'shell', 'execute', 'run_command', 'file_write', 'file_edit', 'make_directory']);
/**
 * Returns a list of warnings for a custom profile that grants dangerous tools.
 * Built-in profiles are trusted and exempt.
 */
function auditCustomProfile(profile) {
    const warnings = [];
    if (profile.allowedTools && profile.allowedTools.length > 0) {
        const dangerous = profile.allowedTools.filter((t) => DANGEROUS_TOOLS.has(t));
        if (dangerous.length > 0) {
            warnings.push(`Profile "${profile.name}" explicitly allows dangerous tools: ${dangerous.join(', ')}. Review before use.`);
        }
    }
    if (profile.blockedTools && profile.blockedTools.length > 0) {
        // Warn if blockedTools is empty (unblocks everything) when allowedTools is also unset
        if (profile.blockedTools.length === 0 && !profile.allowedTools) {
            warnings.push(`Profile "${profile.name}" has empty blockedTools — all tools are accessible.`);
        }
    }
    return warnings;
}
//# sourceMappingURL=configProfiles.js.map