"use strict";
/**
 * test_toolset_enforcement.ts — Stories 10.1 + 10.2
 *
 * Asserts:
 *   1. With OPENLIFE_TOOLSET_ENFORCEMENT unset, every guard returns true / no throw.
 *   2. With enforcement ON + wildcard profile, every guard returns true / no throw.
 *   3. With enforcement ON + restricted profile, guards for missing
 *      toolsets throw ToolsetBlockedError with stable code `toolset_blocked:<cat>`.
 *   4. isToolsetAllowed() never throws and returns false in (3).
 *
 * The test sets OPENLIFE_STATE_DIR to a tmp dir so it never touches real
 * `.openlife/profiles/`.
 */
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"));
function assertTrue(cond, label) {
    if (!cond)
        throw new Error(`ASSERT_FAILED[${label}]`);
}
function withEnv(key, value, body) {
    const prev = process.env[key];
    if (value === undefined)
        delete process.env[key];
    else
        process.env[key] = value;
    try {
        body();
    }
    finally {
        if (prev === undefined)
            delete process.env[key];
        else
            process.env[key] = prev;
    }
}
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'toolset-enf-'));
const prevStateDir = process.env.OPENLIFE_STATE_DIR;
process.env.OPENLIFE_STATE_DIR = tmp;
try {
    // Defer requires until after env is set so ProfileManager picks tmp dir.
    const { ProfileManager } = require('./cli/ProfileManager');
    const { isToolsetAllowed, assertToolsetAllowed, ToolsetBlockedError, _resetToolsetGuardCache, } = require('./orchestrator/toolset/ToolsetGuard');
    const pm = new ProfileManager();
    // ── Scenario 1: enforcement OFF (default) ────────────────────────────
    withEnv('OPENLIFE_TOOLSET_ENFORCEMENT', undefined, () => {
        _resetToolsetGuardCache();
        assertTrue(isToolsetAllowed('terminal') === true, '[10.1] enforcement off → terminal allowed');
        assertTrue(isToolsetAllowed('delegation') === true, '[10.1] enforcement off → delegation allowed');
        let threw = false;
        try {
            assertToolsetAllowed('terminal');
        }
        catch {
            threw = true;
        }
        assertTrue(!threw, '[10.1] enforcement off → assert no throw');
    });
    console.log('[10.1] enforcement OFF: all guards pass ✓');
    // ── Scenario 2: enforcement ON + wildcard profile ────────────────────
    pm.create({ id: 'wild', name: 'wildcard', toolsetAllowed: ['*'] });
    pm.use('wild');
    withEnv('OPENLIFE_TOOLSET_ENFORCEMENT', 'on', () => {
        _resetToolsetGuardCache();
        assertTrue(isToolsetAllowed('terminal') === true, '[10.1] wildcard → terminal allowed');
        assertTrue(isToolsetAllowed('delegation') === true, '[10.2] wildcard → delegation allowed');
        let threw = false;
        try {
            assertToolsetAllowed('terminal');
            assertToolsetAllowed('delegation');
        }
        catch {
            threw = true;
        }
        assertTrue(!threw, '[10.1+10.2] wildcard → assert no throw');
    });
    console.log('[10.1+10.2] enforcement ON with wildcard: all guards pass ✓');
    // ── Scenario 3: enforcement ON + restricted profile (memory only) ────
    pm.create({ id: 'locked', name: 'locked-down', toolsetAllowed: ['memory'] });
    pm.use('locked');
    withEnv('OPENLIFE_TOOLSET_ENFORCEMENT', 'on', () => {
        _resetToolsetGuardCache();
        assertTrue(isToolsetAllowed('memory') === true, '[10.x] locked → memory allowed');
        assertTrue(isToolsetAllowed('terminal') === false, '[10.1] locked → terminal blocked');
        assertTrue(isToolsetAllowed('delegation') === false, '[10.2] locked → delegation blocked');
        // assertToolsetAllowed must throw with the stable code.
        let thrown = null;
        try {
            assertToolsetAllowed('terminal', 'TaskExecutor.runShellCommand');
        }
        catch (err) {
            thrown = err;
        }
        assertTrue(thrown instanceof Error, '[10.1] locked → assert threw');
        assertTrue(thrown instanceof ToolsetBlockedError, '[10.1] locked → ToolsetBlockedError class');
        assertTrue(thrown.code === 'toolset_blocked:terminal', `[10.1] locked → stable code (got ${thrown.code})`);
        let thrown2 = null;
        try {
            assertToolsetAllowed('delegation', 'Brain.thinkWithOpenAICLI');
        }
        catch (err) {
            thrown2 = err;
        }
        assertTrue(thrown2 instanceof ToolsetBlockedError, '[10.2] locked → delegation throws ToolsetBlockedError');
        assertTrue(thrown2.code === 'toolset_blocked:delegation', `[10.2] locked → stable code (got ${thrown2.code})`);
    });
    console.log('[10.1] enforcement ON locked: terminal blocked w/ stable code ✓');
    console.log('[10.2] enforcement ON locked: delegation blocked w/ stable code ✓');
    // ── Scenario 4: per-category blocked codes (Story 20.2, v1.7) ─────────
    // Confirms each of the 11 categories hooked in v1.7 emits its own
    // stable error code. Categories with no production hook today
    // (browser, image_gen) are documented in the v1.7-roadmap and asserted
    // by the registry-side check only — they remain reachable via
    // isToolsetAllowed and will fail closed when a real call site lands.
    const v17Categories = [
        'file', 'web', 'memory', 'mcp', 'vision', 'tts',
        'cron', 'gateway', 'skills', 'workflows', 'squads',
        // browser + image_gen have no production hook today; covered by
        // registry-side guarantee only.
        'browser', 'image_gen',
    ];
    // Use a profile that allowlists only 'terminal' so every v1.7
    // category fails the guard.
    pm.create({ id: 'terminal-only', name: 'terminal-only', toolsetAllowed: ['terminal'] });
    pm.use('terminal-only');
    withEnv('OPENLIFE_TOOLSET_ENFORCEMENT', 'on', () => {
        _resetToolsetGuardCache();
        for (const cat of v17Categories) {
            assertTrue(isToolsetAllowed(cat) === false, `[20.2] terminal-only → ${cat} blocked`);
            let thrown = null;
            try {
                assertToolsetAllowed(cat, `regression:${cat}`);
            }
            catch (err) {
                thrown = err;
            }
            assertTrue(thrown instanceof ToolsetBlockedError, `[20.2] ${cat} throws ToolsetBlockedError`);
            const code = thrown.code;
            assertTrue(code === `toolset_blocked:${cat}`, `[20.2] ${cat} stable code (got ${code})`);
        }
        // sanity: the one allowed category is not blocked
        assertTrue(isToolsetAllowed('terminal') === true, '[20.2] terminal-only → terminal allowed');
    });
    console.log(`[20.2] enforcement ON terminal-only: ${v17Categories.length} categories blocked w/ stable codes ✓`);
    console.log('TEST_TOOLSET_ENFORCEMENT_OK');
}
finally {
    if (prevStateDir === undefined)
        delete process.env.OPENLIFE_STATE_DIR;
    else
        process.env.OPENLIFE_STATE_DIR = prevStateDir;
    fs.rmSync(tmp, { recursive: true, force: true });
}
