"use strict";
// src/orchestrator/CatalogAgentExecutor.ts
// Executes a catalog agent BY LOADING ITS REAL PERSONA from the AGENT.md body.
//
// The 314 catalog agents each ship a rich persona definition in the body of
// .catalog/agents/{id}/AGENT.md (expertise, activation notice, commands). The
// legacy DynamicAgentExecutor threw that away and ran a generic name/role
// prompt. This executor loads the full definition and injects it as the system
// prompt, so the agent actually responds AS itself.
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.CatalogAgentExecutor = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
/** Multi-root resolution mirroring AgentRegistry.defaultAgentRoots():
 *  prefer the user's project-local .catalog, fall back to the package-bundled
 *  one. OPENLIFE_AGENT_ROOTS overrides both. */
function agentRoots() {
    const env = process.env.OPENLIFE_AGENT_ROOTS;
    if (env)
        return env.split(':').filter(Boolean);
    const cwdRoot = path.join(process.cwd(), '.catalog', 'agents');
    const packageRoot = path.join(__dirname, '..', '..', '.catalog', 'agents');
    return cwdRoot === packageRoot ? [cwdRoot] : [cwdRoot, packageRoot];
}
/** Strip the leading mirror front-matter (the `---\n...\n---` block the
 *  catalog importer prepends) so the body starts at the real persona. */
function extractBody(content) {
    const fmMatch = content.match(/^---\n[\s\S]*?\n---\n?/);
    const afterFm = fmMatch ? content.slice(fmMatch[0].length) : content;
    return afterFm.trim();
}
function firstMatch(content, re) {
    const m = content.match(re);
    return m?.[1]?.trim();
}
/** OpenLife method-agent roots (.openlife/method/agents/{id}.md, flat files).
 *  These are the framework's own personas (maestro, builder, sentinel, …) that
 *  workflows reference by name. Mirrors the catalog multi-root resolution. */
function methodRoots() {
    const cwd = path.join(process.cwd(), '.openlife', 'method', 'agents');
    const pkg = path.join(__dirname, '..', '..', '.openlife', 'method', 'agents');
    return cwd === pkg ? [cwd] : [cwd, pkg];
}
function parseDef(id, file, content) {
    const name = firstMatch(content, /^name:\s*(.+)$/m) || id;
    const role = firstMatch(content, /^role:\s*"([^"]+)"/m)
        || firstMatch(content, /^role:\s*(.+)$/m)
        || 'specialist';
    return { id, name, role, body: extractBody(content), sourcePath: file };
}
class CatalogAgentExecutor {
    brain;
    memory;
    recorder;
    /** @param brain    LLM dispatcher (injected for testability).
     *  @param memory   optional memory searcher (Phase 4). When present, relevant
     *                  memory is retrieved for the task and injected as context.
     *  @param recorder optional execution recorder (Phase 6). When present, each
     *                  run's outcome is recorded and proven drafts auto-promote.
     *  Omit memory + recorder and the executor behaves exactly as Phase 1. */
    constructor(brain, memory, recorder) {
        this.brain = brain;
        this.memory = memory;
        this.recorder = recorder;
    }
    /** Load a catalog agent's definition by id. Returns null if not found.
     *  Guards against path traversal — id is treated as a single dir name. */
    loadDefinition(id) {
        // id must be a plain directory name — reject traversal / separators
        if (!id || id.includes('/') || id.includes('\\') || id.includes('..'))
            return null;
        // 1. Catalog agents: .catalog/agents/{id}/AGENT.md (the 314).
        for (const root of agentRoots()) {
            const dir = path.resolve(root, id);
            if (!dir.startsWith(path.resolve(root) + path.sep))
                continue;
            const file = path.join(dir, 'AGENT.md');
            if (!fs.existsSync(file))
                continue;
            try {
                return parseDef(id, file, fs.readFileSync(file, 'utf-8'));
            }
            catch {
                continue;
            }
        }
        // 2. Method agents: .openlife/method/agents/{id}.md (flat). Accept both
        //    "builder" and "openlife-builder" so workflow step ids resolve.
        const candidates = id.startsWith('openlife-') ? [id.slice('openlife-'.length), id] : [id];
        for (const root of methodRoots()) {
            for (const cand of candidates) {
                const file = path.resolve(root, `${cand}.md`);
                if (!file.startsWith(path.resolve(root) + path.sep))
                    continue;
                if (!fs.existsSync(file))
                    continue;
                try {
                    return parseDef(id, file, fs.readFileSync(file, 'utf-8'));
                }
                catch {
                    continue;
                }
            }
        }
        return null;
    }
    /** Build the system prompt that makes the LLM behave as this agent. */
    buildSystemPrompt(def) {
        return [
            `You are ${def.name} (${def.role}), an OpenLife catalog agent.`,
            '',
            'Your full definition follows. Adopt this persona, expertise, and',
            'communication style. Stay in character and apply your specialized',
            'knowledge to the task.',
            '',
            '--- AGENT DEFINITION ---',
            def.body,
            '--- END DEFINITION ---',
            '',
            'Respond to the task as this agent.',
        ].join('\n');
    }
    /** Prepend relevant memory to the task when a memory searcher is wired.
     *  Fails open: any error or empty result leaves the task untouched. */
    async withMemory(task) {
        if (!this.memory)
            return task;
        try {
            const memo = await this.memory.search(task);
            if (memo && memo.trim()) {
                return `[RELEVANT MEMORY]\n${memo.trim()}\n\n[TASK]\n${task}`;
            }
        }
        catch { /* memory is best-effort — never block execution */ }
        return task;
    }
    /** Execute a task as the given catalog agent. */
    async execute(id, task) {
        const def = this.loadDefinition(id);
        if (!def) {
            return { ok: false, agentId: id, error: 'agent_not_found' };
        }
        if (!task || !task.trim()) {
            return { ok: false, agentId: id, error: 'task_required' };
        }
        try {
            const systemPrompt = this.buildSystemPrompt(def);
            const userMessage = await this.withMemory(task);
            const output = await this.brain.think(systemPrompt, userMessage);
            await this.recordOutcome(id, true);
            return { ok: true, agentId: id, output };
        }
        catch (err) {
            await this.recordOutcome(id, false);
            return {
                ok: false,
                agentId: id,
                error: err instanceof Error ? err.message : String(err),
            };
        }
    }
    /** Best-effort feedback recording — never blocks or fails the execution. */
    async recordOutcome(id, success) {
        if (!this.recorder)
            return;
        try {
            await this.recorder.recordExecution(id, success);
        }
        catch { /* best-effort */ }
    }
}
exports.CatalogAgentExecutor = CatalogAgentExecutor;
