"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.prefillApplicationDefinitionFromApplicationSecurityConfig = prefillApplicationDefinitionFromApplicationSecurityConfig;
exports.applicationDefinitionToPurpose = applicationDefinitionToPurpose;
exports.getSecurityConfigArgusChecklistSnapshotFromCurrentConfig = getSecurityConfigArgusChecklistSnapshotFromCurrentConfig;
exports.buildDraftSecurityConfigPayload = buildDraftSecurityConfigPayload;
exports.getSecurityConfigArgusTools = getSecurityConfigArgusTools;
const logger_1 = __importDefault(require("../../../../logger"));
const handleApplicationToProviderFormat_1 = require("../../../../util/handleApplicationToProviderFormat");
const plugins_1 = require("../../../../redteam/constants/plugins");
const strategies_1 = require("../../../../redteam/constants/strategies");
const metadata_1 = require("../../../../redteam/constants/metadata");
const PLUGIN_SET = new Set(plugins_1.ALL_PLUGINS);
const STRATEGY_SET = new Set(strategies_1.ALL_STRATEGIES);
const normalizeIdLike = (raw) => String(raw || '')
    .trim()
    .toLowerCase()
    .replace(/['"]/g, '')
    .replace(/\s+/g, '-')
    .replace(/_/g, '-');
const CANONICAL_ID_BY_NORMALIZED = (() => {
    const map = new Map();
    const add = (id) => {
        if (!id) {
            return;
        }
        map.set(normalizeIdLike(id), id);
    };
    for (const id of plugins_1.ALL_PLUGINS) {
        add(String(id));
    }
    for (const id of strategies_1.ALL_STRATEGIES) {
        add(String(id));
    }
    for (const id of Object.keys(metadata_1.subCategoryDescriptions)) {
        add(String(id));
    }
    return map;
})();
function idsFromConfigArray(arr) {
    if (!Array.isArray(arr)) {
        return [];
    }
    return arr
        .map((x) => {
        if (typeof x === 'string') {
            return x;
        }
        if (x && typeof x === 'object' && 'id' in x) {
            return String(x.id);
        }
        return '';
    })
        .map((s) => String(s || '').trim())
        .filter(Boolean);
}
const APPLICATION_DEFINITION_STRING_KEYS = [
    'purpose',
    'hasAccessTo',
    'doesNotHaveAccessTo',
    'userTypes',
    'securityRequirements',
    'sensitiveDataTypes',
    'exampleIdentifiers',
    'criticalActions',
    'forbiddenTopics',
    'competitors',
    'redteamUser',
    'features',
    'industry',
    'systemPrompt',
    'attackConstraints',
];
const DEFAULT_HTTP_PLACEHOLDER = {
    id: 'http',
    config: {
        url: '',
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ message: '{{prompt}}' }),
    },
};
function normalizeApplicationDefinition(raw) {
    const out = {};
    if (!raw || typeof raw !== 'object') {
        for (const k of APPLICATION_DEFINITION_STRING_KEYS) {
            out[k] = '';
        }
        return out;
    }
    const o = raw;
    for (const k of APPLICATION_DEFINITION_STRING_KEYS) {
        const v = o[k];
        out[k] = typeof v === 'string' ? v : '';
    }
    return out;
}
function mergeApplicationDefinitionPreserveNonEmpty(existing, incoming) {
    const base = normalizeApplicationDefinition(existing);
    const next = normalizeApplicationDefinition(incoming);
    for (const k of APPLICATION_DEFINITION_STRING_KEYS) {
        const v = next[k];
        if (typeof v === 'string' && v.trim()) {
            base[k] = v;
        }
    }
    return base;
}
/** Merge `applications.security_config` into draft `config.applicationDefinition` only where the client sent empty strings. */
function prefillApplicationDefinitionFromApplicationSecurityConfig(args) {
    const merged = normalizeApplicationDefinition(args.incomingApplicationDefinition);
    const fromApp = normalizeApplicationDefinition(args.applicationSecurityConfig);
    for (const k of APPLICATION_DEFINITION_STRING_KEYS) {
        if (!merged[k].trim() && fromApp[k].trim()) {
            merged[k] = fromApp[k];
        }
    }
    return merged;
}
/** Mirrors UI `applicationDefinitionToPurpose` (useRedTeamConfig) for the saved `config.purpose` field. */
function applicationDefinitionToPurpose(applicationDefinition) {
    const sections = [];
    const push = (title, body) => {
        if (body?.trim()) {
            sections.push(`${title}:\n\`\`\`\n${body.trim()}\n\`\`\``);
        }
    };
    push('Application Purpose', applicationDefinition.purpose);
    push('Systems and Data the Application Has Access To', applicationDefinition.hasAccessTo);
    push('Systems and Data the Application Should NOT Have Access To', applicationDefinition.doesNotHaveAccessTo);
    push('Types of Users Who Interact with the Application', applicationDefinition.userTypes);
    push('Security and Compliance Requirements', applicationDefinition.securityRequirements);
    push('Types of Sensitive Data Handled', applicationDefinition.sensitiveDataTypes);
    push('Example Data Identifiers and Formats', applicationDefinition.exampleIdentifiers);
    push('Critical or Dangerous Actions the Application Can Perform', applicationDefinition.criticalActions);
    push('Content and Topics the Application Should Never Discuss', applicationDefinition.forbiddenTopics);
    push('Competitors That Should Not Be Endorsed', applicationDefinition.competitors);
    push('Red Team User Persona', applicationDefinition.redteamUser);
    return sections.join('\n\n');
}
function resolveTarget(filters, explicitTarget, applicationDefinition) {
    if (explicitTarget && typeof explicitTarget === 'object' && !Array.isArray(explicitTarget)) {
        return { target: explicitTarget, source: 'tool_argument_target' };
    }
    const app = filters.application;
    if (app && typeof app === 'object') {
        try {
            const provider = (0, handleApplicationToProviderFormat_1.convertApplicationToProviderFormat)(app);
            if (provider && typeof provider === 'object') {
                return { target: provider, source: 'filters.application' };
            }
        }
        catch (e) {
            logger_1.default.warn(`securityConfig draft: convertApplicationToProviderFormat failed: ${e}`);
        }
    }
    const label = typeof applicationDefinition.purpose === 'string' && applicationDefinition.purpose.trim()
        ? applicationDefinition.purpose.trim().slice(0, 120)
        : 'draft-target';
    return {
        target: { ...DEFAULT_HTTP_PLACEHOLDER, label },
        source: 'placeholder_http',
    };
}
/** Same semantics as the app `computeChecks` (SecurityChecklistCard / CreateSecurityConfigWithArgusController). */
function computeSecurityConfigArgusChecklistMissing(args) {
    const ad = args.applicationDefinition;
    const hasPurpose = Boolean(ad.purpose?.trim());
    const hasAccess = Boolean(ad.hasAccessTo?.trim());
    const hasRestrictions = Boolean(ad.doesNotHaveAccessTo?.trim() || ad.forbiddenTopics?.trim());
    const hasSensitive = Boolean(ad.sensitiveDataTypes?.trim() || ad.exampleIdentifiers?.trim());
    const hasCompliance = Boolean(ad.securityRequirements?.trim() || ad.criticalActions?.trim());
    const hasPersona = Boolean(ad.redteamUser?.trim() || ad.userTypes?.trim());
    const checks = {
        purposeAndAccess: hasPurpose && hasAccess,
        restrictions: hasRestrictions,
        sensitiveData: hasSensitive,
        highRiskAndCompliance: hasCompliance,
        userPersona: hasPersona,
        vulnerableCategories: args.pluginCount > 0,
        attackStrategies: args.strategyCount > 0,
    };
    const checklistMissingKeys = Object.keys(checks).filter((k) => !checks[k]);
    return { checklistComplete: checklistMissingKeys.length === 0, checklistMissingKeys };
}
/** Pre-turn checklist for Argus context (matches UI + `draft_security_config` tool). */
function getSecurityConfigArgusChecklistSnapshotFromCurrentConfig(currentConfig) {
    if (!currentConfig || typeof currentConfig !== 'object') {
        const empty = normalizeApplicationDefinition(undefined);
        return computeSecurityConfigArgusChecklistMissing({
            applicationDefinition: empty,
            pluginCount: 0,
            strategyCount: 0,
        });
    }
    const cfg = currentConfig;
    const ad = normalizeApplicationDefinition(cfg.applicationDefinition);
    const pluginCount = idsFromConfigArray(cfg.plugins).length;
    const strategyCount = idsFromConfigArray(cfg.strategies).length;
    return computeSecurityConfigArgusChecklistMissing({
        applicationDefinition: ad,
        pluginCount,
        strategyCount,
    });
}
function buildDraftSecurityConfigPayload(input) {
    const filters = (input.filters && typeof input.filters === 'object' ? input.filters : {});
    const currentConfig = filters?.currentConfig && typeof filters.currentConfig === 'object'
        ? filters.currentConfig
        : null;
    // Always merge non-empty `applicationDefinition` fields from the tool input into the current
    // draft. Gating updates behind `allowApplicationDefinitionUpdate` broke the first user turn
    // (free text before any ```argus_ui``` message), so fields the user stated were dropped.
    const applicationDefinition = mergeApplicationDefinitionPreserveNonEmpty(currentConfig ? currentConfig.applicationDefinition : undefined, input.applicationDefinition);
    const validationWarnings = [];
    const followUpSuggestions = [];
    // When the model passes a **non-empty** `plugins` / `strategies` array, treat it as the
    // **authoritative final selection** for that field (removals must stick). Unioning with
    // currentConfig would re-append removed ids. Omit the key or pass `[]` to preserve the prior
    // draft for that field.
    const rawPlugins = Array.isArray(input.plugins) && input.plugins.length > 0
        ? input.plugins.map(String)
        : currentConfig
            ? idsFromConfigArray(currentConfig.plugins)
            : [];
    const plugins = [];
    const normalizedRiskCategoryByLower = new Map(Object.keys(metadata_1.riskCategories).map((k) => [k.toLowerCase(), k]));
    const addPluginId = (id) => {
        const trimmed = String(id || '').trim();
        const canonical = PLUGIN_SET.has(trimmed) ? trimmed : CANONICAL_ID_BY_NORMALIZED.get(normalizeIdLike(trimmed));
        if (canonical && PLUGIN_SET.has(canonical)) {
            plugins.push(canonical);
            return;
        }
        validationWarnings.push(`Unknown plugin id (dropped): ${id}`);
    };
    for (const raw of rawPlugins) {
        const id = String(raw || '').trim();
        if (!id) {
            continue;
        }
        const direct = PLUGIN_SET.has(id) ? id : CANONICAL_ID_BY_NORMALIZED.get(normalizeIdLike(id));
        if (direct && PLUGIN_SET.has(direct)) {
            plugins.push(direct);
            continue;
        }
        // Support passing category names instead of ids:
        // - "Security Controls"
        // - "category:Security Controls"
        const categoryNameRaw = id.toLowerCase().startsWith('category:') ? id.slice('category:'.length).trim() : id;
        const canonical = normalizedRiskCategoryByLower.get(categoryNameRaw.toLowerCase());
        const group = canonical ? metadata_1.riskCategories[canonical] : null;
        if (canonical && Array.isArray(group) && group.length > 0) {
            for (const pid of group) {
                addPluginId(String(pid));
            }
            continue;
        }
        validationWarnings.push(`Unknown plugin id (dropped): ${id}`);
    }
    // De-dupe while preserving order
    const seen = new Set();
    const deduped = plugins.filter((p) => (seen.has(p) ? false : (seen.add(p), true)));
    const rawStrategies = Array.isArray(input.strategies) && input.strategies.length > 0
        ? input.strategies.map(String)
        : currentConfig
            ? idsFromConfigArray(currentConfig.strategies)
            : [];
    const strategies = [];
    for (const id of rawStrategies) {
        const trimmed = String(id || '').trim();
        const canonical = STRATEGY_SET.has(trimmed)
            ? trimmed
            : CANONICAL_ID_BY_NORMALIZED.get(normalizeIdLike(trimmed));
        if (canonical && STRATEGY_SET.has(canonical)) {
            strategies.push(canonical);
        }
        else {
            validationWarnings.push(`Unknown strategy id (dropped): ${id}`);
        }
    }
    const seenStrategies = new Set();
    const dedupedStrategies = strategies.filter((s) => seenStrategies.has(s) ? false : (seenStrategies.add(s), true));
    const { target, source } = resolveTarget(filters, input.target, applicationDefinition);
    // NOTE: Do not add tool-generated follow-ups. The Argus model should ask questions via `argus_ui`
    // so the UI structure stays consistent and driven by the assistant response.
    const description = typeof input.description === 'string' && input.description.trim()
        ? input.description.trim()
        : 'Security configuration (draft)';
    const numTests = typeof input.numTests === 'number' && Number.isFinite(input.numTests) && input.numTests > 0
        ? Math.floor(input.numTests)
        : 2;
    const language = typeof input.language === 'string' && input.language.trim() ? input.language.trim() : 'English (US)';
    const prompts = Array.isArray(input.prompts) && input.prompts.length > 0
        ? input.prompts.map((p) => String(p))
        : ['{{prompt}}'];
    const testGenerationInstructions = typeof input.testGenerationInstructions === 'string' ? input.testGenerationInstructions : '';
    const entities = Array.isArray(input.entities) ? input.entities.map(String) : [];
    const purpose = applicationDefinitionToPurpose(applicationDefinition);
    const { checklistComplete, checklistMissingKeys } = computeSecurityConfigArgusChecklistMissing({
        applicationDefinition,
        pluginCount: deduped.length,
        strategyCount: dedupedStrategies.length,
    });
    const config = {
        description,
        prompts,
        target,
        plugins: deduped,
        strategies: dedupedStrategies,
        purpose,
        entities,
        numTests,
        language,
        testGenerationInstructions,
        applicationDefinition,
        defaultTest: undefined,
    };
    return {
        config,
        validationWarnings,
        followUpSuggestions,
        targetSource: source,
        vulnerabilityCategoryNames: Object.keys(metadata_1.riskCategories),
        checklistComplete,
        checklistMissingKeys,
    };
}
function createTool(name, description, schema, execute) {
    return { name, description, schema, execute };
}
/**
 * Argus tools for the `securityconfig` domain: build JSON matching persisted security `config` (UI parity).
 */
function getSecurityConfigArgusTools() {
    const draftSecurityConfigSchema = {
        type: 'object',
        properties: {
            description: {
                type: 'string',
                description: 'Short human-readable name/summary for this configuration.',
            },
            applicationDefinition: {
                type: 'object',
                description: 'Free-text fields describing the app under test (purpose, access boundaries, users, sensitive data, etc.). All values should be strings.',
            },
            plugins: {
                type: 'array',
                items: { type: 'string' },
                description: 'When non-empty, replaces draft plugin ids (full desired list). Omit or [] keeps prior plugins. Valid catalog ids; category names expand to ids.',
            },
            strategies: {
                type: 'array',
                items: { type: 'string' },
                description: 'When non-empty, replaces draft strategy ids (full desired list). Omit or [] keeps prior strategies. To remove one, omit its id and re-list every id to keep.',
            },
            numTests: { type: 'number', description: 'Probes per plugin (UI default often 2–10).' },
            language: { type: 'string', description: 'Natural language for generated tests (e.g. English (US)).' },
            prompts: {
                type: 'array',
                items: { type: 'string' },
                description: 'Template prompts; usually includes {{prompt}} placeholder.',
            },
            testGenerationInstructions: {
                type: 'string',
                description: 'Extra instructions for the red team generator.',
            },
            entities: {
                type: 'array',
                items: { type: 'string' },
                description: 'Optional entity identifiers for context.',
            },
            target: {
                type: 'object',
                description: 'Optional provider `target` object (id, label, config). If omitted, uses `filters.application` from Argus context when present, else HTTP placeholder.',
            },
        },
    };
    return [
        createTool('draft_security_config', 'Build a draft security evaluation `config` JSON matching the Red Team security setup UI (plugins, strategies, applicationDefinition, target, prompts, purpose, etc.). **plugins** and **strategies** arguments **replace** the prior draft for that field when passed as a **non-empty** array (pass the full desired ids; omit the key or pass [] to leave that field unchanged). Returns **checklistComplete** / **checklistMissingKeys** (same rules as the UI checklist). When **checklistComplete** is true: do **not** emit `argus_ui.suggestions` and do **not** ask about starting/running red team evaluations or generic “additional adjustments”—users use Review/Save/Run in the product. Otherwise ask follow-ups (**checklist fields only**: applicationDefinition gaps, plugins/vulnerability categories, strategies/attack approach, required audio strategy config)—never follow-ups about evaluation settings users edit manually (numTests, maxConcurrency, language, testGenerationInstructions, prompts, description, entities, target).', draftSecurityConfigSchema, async (input) => {
            try {
                const filters = input &&
                    typeof input.filters === 'object' &&
                    input.filters !== null &&
                    !Array.isArray(input.filters)
                    ? input.filters
                    : {};
                // Guardrail: do not let the model change the target once one exists.
                // Source of truth is either an explicit lockedTarget provided by backend,
                // or the currentConfig.target snapshot if present.
                const lockedTargetRaw = filters?.lockedTarget ??
                    (filters?.currentConfig &&
                        typeof filters.currentConfig === 'object' &&
                        filters.currentConfig !== null
                        ? filters.currentConfig.target
                        : undefined);
                const lockedTarget = lockedTargetRaw && typeof lockedTargetRaw === 'object' && !Array.isArray(lockedTargetRaw)
                    ? lockedTargetRaw
                    : undefined;
                const payload = buildDraftSecurityConfigPayload({
                    filters,
                    description: input.description,
                    applicationDefinition: input.applicationDefinition,
                    plugins: input.plugins,
                    strategies: input.strategies,
                    numTests: input.numTests,
                    language: input.language,
                    prompts: input.prompts,
                    testGenerationInstructions: input.testGenerationInstructions,
                    entities: input.entities,
                    // If locked target exists, always use it.
                    target: lockedTarget ?? input.target,
                });
                return JSON.stringify(payload);
            }
            catch (error) {
                logger_1.default.error(`draft_security_config tool error: ${error}`);
                return JSON.stringify({
                    error: error instanceof Error ? error.message : String(error),
                });
            }
        }),
    ];
}
//# sourceMappingURL=securityConfigArgusTools.js.map