"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadAgentMinds = loadAgentMinds;
/**
 * Module: minds/load
 *
 * Loads agent minds/persona/knowhow/pitfalls and memory files from `.minds`.
 * Composes the system prompt and aggregates memories with optional hints.
 */
const time_1 = require("@longrun-ai/kernel/utils/time");
const promises_1 = require("fs/promises");
const path_1 = __importDefault(require("path"));
const runtime_1 = require("../apps/runtime");
const dialog_1 = require("../dialog");
const log_1 = require("../log");
const supervisor_1 = require("../mcp/supervisor");
const rtws_1 = require("../rtws");
const work_language_1 = require("../runtime/work-language");
const load_1 = require("../skills/load");
const team_1 = require("../team");
const ctrl_1 = require("../tools/ctrl");
const shell_tools_1 = require("../tools/shell-tools");
const skills_1 = require("../tools/skills");
const toolset_manual_1 = require("../tools/toolset-manual");
const minds_i18n_1 = require("./minds-i18n");
const system_prompt_1 = require("./system-prompt");
const system_prompt_parts_1 = require("./system-prompt-parts");
const TASKDOC_MUTATION_TOOL_NAMES = new Set([
    ctrl_1.doMindTool.name,
    ctrl_1.mindMoreTool.name,
    ctrl_1.changeMindTool.name,
    ctrl_1.neverMindTool.name,
]);
async function readAgentMindResult(id, fn, source) {
    const mindFn = source === 'rtws'
        ? path_1.default.join('.minds', 'team', id, fn)
        : path_1.default.join(__dirname, 'builtin', id, fn);
    try {
        await (0, promises_1.access)(mindFn);
        const text = await (0, promises_1.readFile)(mindFn, 'utf-8');
        return { kind: 'found', text };
    }
    catch (error) {
        if (typeof error === 'object' &&
            error !== null &&
            'code' in error &&
            error.code === 'ENOENT') {
            return { kind: 'missing' };
        }
        log_1.log.warn(`Failed reading file '${mindFn}':`, error);
        return { kind: 'missing' };
    }
}
async function readAgentMindPreferred(options) {
    for (const fn of options.rtwsPreferredFilenames) {
        const result = await readAgentMindResult(options.id, fn, 'rtws');
        if (result.kind === 'found')
            return result.text;
    }
    const builtinPreferredFilenames = options.builtinPreferredFilenames ?? options.rtwsPreferredFilenames;
    for (const fn of builtinPreferredFilenames) {
        const result = await readAgentMindResult(options.id, fn, 'builtin');
        if (result.kind === 'found')
            return result.text;
    }
    return options.noFileDefault;
}
async function readMindsTextResult(fn) {
    const mindFn = path_1.default.join('.minds', fn);
    try {
        const st = await (0, promises_1.stat)(mindFn);
        if (!st.isFile())
            return { kind: 'missing' };
    }
    catch (error) {
        if (typeof error === 'object' &&
            error !== null &&
            'code' in error &&
            error.code === 'ENOENT') {
            return { kind: 'missing' };
        }
        log_1.log.warn(`Failed stating file '${mindFn}':`, error);
        return { kind: 'missing' };
    }
    try {
        const text = await (0, promises_1.readFile)(mindFn, 'utf-8');
        return { kind: 'found', text };
    }
    catch (error) {
        if (typeof error === 'object' &&
            error !== null &&
            'code' in error &&
            error.code === 'ENOENT') {
            return { kind: 'missing' };
        }
        log_1.log.warn(`Failed reading file '${mindFn}':`, error);
        return { kind: 'missing' };
    }
}
async function readMindsTextPreferred(options) {
    for (const fn of options.preferredFilenames) {
        const result = await readMindsTextResult(fn);
        if (result.kind === 'found')
            return result.text;
    }
    return options.noFileDefault;
}
function buildAgentMcpToolsetRuntimeNotices(params) {
    if (params.declaredToolsets.kind !== 'loaded')
        return [];
    const runtimeStatusByServerId = new Map((0, supervisor_1.getMcpDeclaredServerRuntimeStatuses)().map((status) => [status.serverId, status]));
    const notices = [];
    for (const toolsetName of team_1.Team.listExplicitToolsets(params.agent)) {
        if (!params.declaredToolsets.declaredServerIds.has(toolsetName))
            continue;
        if (params.declaredToolsets.invalidServerIds.has(toolsetName))
            continue;
        const runtimeStatus = runtimeStatusByServerId.get(toolsetName);
        if (runtimeStatus?.status === 'loaded')
            continue;
        notices.push({
            toolsetName,
            transport: runtimeStatus?.transport ?? 'unknown',
            status: runtimeStatus?.status === 'disabled'
                ? 'disabled'
                : runtimeStatus?.status === 'config_invalid'
                    ? 'config_invalid'
                    : 'temporarily_unavailable',
            errorText: runtimeStatus?.errorText,
        });
    }
    return notices;
}
async function loadAgentMinds(agentId, dialog, options) {
    const workingLanguage = (0, work_language_1.getWorkLanguage)();
    const missingToolsetPolicy = options?.missingToolsetPolicy ?? 'warn';
    const rtwsRootAbs = (0, rtws_1.domindsRtwsRootAbs)();
    try {
        await (0, runtime_1.registerEnabledAppsToolProxies)({ rtwsRootAbs });
    }
    catch (error) {
        log_1.log.warn(`Failed to refresh enabled app tool proxies before loading agent minds: ${error instanceof Error ? error.message : String(error)}`);
    }
    let team = await team_1.Team.load();
    const agent = agentId === undefined ? team.getDefaultResponder() : team.getMember(agentId);
    if (!agent)
        throw new Error(`No such agent in team: '${agentId}'`);
    const declaredMcpToolsets = await team_1.Team.readMcpDeclaredToolsets();
    const declaredMcpToolsetNames = declaredMcpToolsets.kind === 'loaded' ? declaredMcpToolsets.declaredServerIds : undefined;
    const invalidMcpToolsetNames = declaredMcpToolsets.kind === 'loaded' ? declaredMcpToolsets.invalidServerIds : undefined;
    // read disk file afresh, in case the contents have changed by human or ai meanwhile
    const personaRaw = await readAgentMindPreferred({
        id: agent.id,
        rtwsPreferredFilenames: [`persona.${workingLanguage}.md`, 'persona.md'],
        noFileDefault: (0, minds_i18n_1.defaultPersonaText)(workingLanguage),
    });
    const knowhowRaw = await readAgentMindPreferred({
        id: agent.id,
        rtwsPreferredFilenames: [`knowhow.${workingLanguage}.md`, 'knowhow.md'],
        builtinPreferredFilenames: [`knowhow.${workingLanguage}.md`, 'knowhow.md'],
        noFileDefault: '',
    });
    const pitfallsRaw = await readAgentMindPreferred({
        id: agent.id,
        rtwsPreferredFilenames: [`pitfalls.${workingLanguage}.md`, 'pitfalls.md'],
        builtinPreferredFilenames: [`pitfalls.${workingLanguage}.md`, 'pitfalls.md'],
        noFileDefault: '',
    });
    const envIntroRaw = await readMindsTextPreferred({
        preferredFilenames: [`env.${workingLanguage}.md`, 'env.md'],
        noFileDefault: '',
    });
    const none = (0, minds_i18n_1.noneText)(workingLanguage);
    const persona = personaRaw && personaRaw.trim() !== '' ? personaRaw : none;
    const knowhow = knowhowRaw && knowhowRaw.trim() !== '' ? knowhowRaw : none;
    const pitfalls = pitfallsRaw && pitfallsRaw.trim() !== '' ? pitfallsRaw : none;
    const envIntro = envIntroRaw && envIntroRaw.trim() !== '' ? envIntroRaw : '';
    // Introduction of all team members (mark "(self)" for the current agent)
    const teamIntro = (0, system_prompt_1.formatTeamIntro)(team, agent.id, workingLanguage);
    // Compose tool list from member's resolved toolsets and tools + built-in human tool.
    // Get base tools from agent; runtime injects allowed intrinsic dialog-control tools afterward.
    // shell_specialists is intended for visible teammates only. Hidden members are exempt from this
    // policy and may carry shell tools.
    const agentIsShellSpecialist = team.shellSpecialists.includes(agent.id) || agent.hidden === true;
    const dynamicToolsetNames = await team_1.Team.listDynamicToolsetNamesForMember({
        member: agent,
        taskDocPath: dialog?.taskDocPath,
    });
    const baseAgentTools = (() => {
        const tools = agent.listTools({
            onMissingToolset: missingToolsetPolicy,
            dynamicToolsetNames,
            declaredMcpToolsetNames,
            invalidMcpToolsetNames,
        });
        if (agentIsShellSpecialist)
            return tools;
        return tools.filter((t) => !(t.type === 'func' && typeof t.name === 'string' && (0, shell_tools_1.isShellToolName)(t.name)));
    })();
    const visibleMcpServerIds = new Set(agent
        .listResolvedToolsetNames({
        onMissing: 'silent',
        dynamicToolsetNames,
        declaredMcpToolsetNames,
        invalidMcpToolsetNames,
    })
        .filter((toolsetName) => declaredMcpToolsetNames?.has(toolsetName)));
    const workspaceSkills = await (0, load_1.loadWorkspaceSkills)({
        rtwsRootAbs,
        memberId: agent.id,
        language: workingLanguage,
        visibleMcpServerIds,
    });
    const skillsText = (0, load_1.renderWorkspaceSkillsPrompt)({
        language: workingLanguage,
        skills: workspaceSkills,
    });
    // Inject intrinsic dialog-control tools as function tools according to dialog scope.
    const intrinsicFuncTools = [
        ctrl_1.addReminderTool,
        ctrl_1.deleteReminderTool,
        ctrl_1.updateReminderTool,
        ctrl_1.migrateReminderTool,
        ctrl_1.clearMindTool,
        ctrl_1.recallTaskdocTool,
    ];
    // Taskdoc mutation tools are only available in main dialogs (not sideDialogs).
    if (dialog === undefined || dialog.askerDialog === undefined) {
        intrinsicFuncTools.push(ctrl_1.doMindTool);
        intrinsicFuncTools.push(ctrl_1.mindMoreTool);
        intrinsicFuncTools.push(ctrl_1.changeMindTool);
        intrinsicFuncTools.push(ctrl_1.neverMindTool);
    }
    const shouldHideTaskdocMutationTools = dialog !== undefined && dialog.askerDialog !== undefined;
    const agentTools = (() => {
        const out = [...baseAgentTools];
        const seenNames = new Set(out.map((t) => t.name));
        for (const t of intrinsicFuncTools) {
            if (!seenNames.has(t.name)) {
                out.push(t);
                seenNames.add(t.name);
            }
        }
        if (!shouldHideTaskdocMutationTools)
            return out;
        return out.filter((t) => !TASKDOC_MUTATION_TOOL_NAMES.has(t.name));
    })();
    const toolsetNames = agent
        .listResolvedToolsetNames({
        onMissing: missingToolsetPolicy,
        dynamicToolsetNames,
        declaredMcpToolsetNames,
        invalidMcpToolsetNames,
    })
        .filter((name) => {
        if (name === 'os')
            return agentIsShellSpecialist;
        return true;
    });
    const manualTools = (0, toolset_manual_1.buildToolsetManualTools)({
        toolsetNames,
        existingToolNames: new Set(agentTools.map((t) => t.name)),
    });
    if (manualTools.tools.length > 0) {
        agentTools.push(...manualTools.tools);
    }
    if (!agentTools.some((tool) => tool.name === skills_1.readSkillTool.name)) {
        agentTools.push(skills_1.readSkillTool);
    }
    const funcTools = agentTools.filter((t) => t.type === 'func');
    const agentHasShellTools = funcTools.some((t) => (0, shell_tools_1.isShellToolName)(t.name));
    const agentHasReadonlyShell = funcTools.some((t) => t.name === 'readonly_shell');
    const shellSpecialistMemberIds = (0, shell_tools_1.listShellSpecialistMemberIds)(team);
    const TEAM_MEMORY_TOOL_NAMES = [
        'add_team_memory',
        'replace_team_memory',
        'drop_team_memory',
        'clear_team_memory',
    ];
    const PERSONAL_MEMORY_TOOL_NAMES = [
        'add_personal_memory',
        'replace_personal_memory',
        'drop_personal_memory',
        'clear_personal_memory',
    ];
    function isTeamMemoryToolName(name) {
        return TEAM_MEMORY_TOOL_NAMES.includes(name);
    }
    function isPersonalMemoryToolName(name) {
        return PERSONAL_MEMORY_TOOL_NAMES.includes(name);
    }
    const agentHasTeamMemoryTools = funcTools.some((t) => isTeamMemoryToolName(t.name));
    const agentHasPersonalMemoryTools = funcTools.some((t) => isPersonalMemoryToolName(t.name));
    const isSideDialog = dialog !== undefined && dialog.askerDialog !== undefined;
    const taskdocMaintainerId = dialog && dialog instanceof dialog_1.SideDialog ? dialog.mainDialog.agentId : agent.id;
    const contextHealthSnapshot = dialog?.getLastContextHealth();
    const contextHealthPromptMode = contextHealthSnapshot &&
        contextHealthSnapshot.kind === 'available' &&
        contextHealthSnapshot.level === 'critical'
        ? 'critical'
        : contextHealthSnapshot &&
            contextHealthSnapshot.kind === 'available' &&
            contextHealthSnapshot.level === 'caution'
            ? 'caution'
            : 'normal';
    const promptdocContext = {
        language: workingLanguage,
        agentId: agent.id,
        isSideDialog,
        taskdocMaintainerId,
        agentHasTeamMemoryTools,
        agentHasPersonalMemoryTools,
        agentIsShellSpecialist,
        agentHasShellTools,
        agentHasReadonlyShell,
        shellSpecialistMemberIds,
        contextHealthPromptMode,
    };
    const policyText = [
        (0, system_prompt_parts_1.buildShellPolicyPrompt)(promptdocContext),
        (0, system_prompt_parts_1.buildMemorySystemPrompt)(promptdocContext),
        (0, minds_i18n_1.taskdocCanonicalCopy)(workingLanguage),
    ]
        .filter((b) => b.trim() !== '')
        .join('\n\n');
    const intrinsicToolUsageText = (0, system_prompt_parts_1.buildIntrinsicToolUsageText)(workingLanguage, intrinsicFuncTools);
    const toolsetManualIntro = (0, toolset_manual_1.formatToolsetManualIntro)(workingLanguage, manualTools.toolNames, toolsetNames);
    const mcpToolsetRuntimeNote = (0, system_prompt_1.formatMcpToolsetRuntimeNote)(workingLanguage, buildAgentMcpToolsetRuntimeNotices({
        agent,
        declaredToolsets: declaredMcpToolsets,
    }));
    const funcToolRulesText = funcTools.length > 0 ? (0, minds_i18n_1.funcToolRulesText)(workingLanguage) : '';
    const systemPrompt = (0, system_prompt_1.buildSystemPrompt)({
        language: workingLanguage,
        dialogScope: isSideDialog ? 'sideDialog' : 'mainDialog',
        contextHealthPromptMode,
        agent,
        persona,
        knowhow,
        pitfalls,
        skillsText,
        envIntro,
        rtwsRootAbs,
        teamIntro,
        funcToolRulesText,
        policyText,
        intrinsicToolUsageText,
        toolsetManualIntro,
        mcpToolsetRuntimeNote,
    });
    // composite this list by reading:
    //   - .minds/memory/team_shared/**/*.md
    //   - .minds/memory/individual/<agent.id>/**/*.md
    // one role='assistant' msg per file, add preamble about it's shared or personal
    // lightly hint the use of memory tools on personal mem, in 1st person tongue,
    // for mem tools made available to the agent per its team member spec
    //
    // aggregate shared and personal memories into assistant messages
    const memories = [];
    const groupedMemories = {
        shared: [],
        personal: [],
    };
    try {
        async function collectMdFiles(rootDir) {
            const files = [];
            async function walk(dir) {
                let entries;
                try {
                    entries = await (0, promises_1.readdir)(dir, { withFileTypes: true });
                }
                catch (err) {
                    if (typeof err === 'object' &&
                        err !== null &&
                        'code' in err &&
                        err.code === 'ENOENT') {
                        return;
                    }
                    log_1.log.warn(`Failed to read directory ${dir}:`, err);
                    return;
                }
                for (const entry of entries) {
                    const fullPath = path_1.default.join(dir, entry.name);
                    if (entry.isDirectory()) {
                        await walk(fullPath);
                    }
                    else if (entry.isFile() && fullPath.endsWith('.md')) {
                        const relativePath = path_1.default.relative(rootDir, fullPath);
                        files.push({
                            absolutePath: fullPath,
                            relativePath: relativePath,
                        });
                    }
                }
            }
            await walk(rootDir);
            return files;
        }
        const sharedFiles = await collectMdFiles('.minds/memory/team_shared');
        const personalFiles = await collectMdFiles(`.minds/memory/individual/${agent.id}`);
        function countWords(text) {
            const trimmed = text.trim();
            if (trimmed === '')
                return 0;
            return trimmed.split(/\s+/).length;
        }
        const readMemFiles = async (files, scopeKind, scopeLabel, relevantToolNames) => {
            for (const file of files) {
                try {
                    const content = await (0, promises_1.readFile)(file.absolutePath, 'utf-8');
                    const s = await (0, promises_1.stat)(file.absolutePath);
                    const lastModified = (0, time_1.formatUnifiedTimestamp)(new Date(s.mtimeMs));
                    const byteSize = s.size;
                    const wordCount = countWords(content);
                    const labels = (0, minds_i18n_1.memoryPreambleLabels)(workingLanguage);
                    let preamble = `### ${scopeLabel}\n- ${labels.pathLabel}: \`${file.relativePath}\`\n- ${labels.lastModifiedLabel}: ${lastModified}\n- ${labels.sizeLabel}: ${byteSize} ${labels.bytesUnit}\n- ${labels.wordsLabel}: ${wordCount}`;
                    // Add tool usage hints if relevant memory tools are available
                    const availableTools = agentTools.map((tool) => tool.name).filter(Boolean);
                    const availableRelevantTools = relevantToolNames.filter((toolName) => availableTools.includes(toolName));
                    if (availableRelevantTools.length > 0) {
                        const toolHints = availableRelevantTools.map((toolName) => `@${toolName} ${file.relativePath}`);
                        const nestedHints = toolHints.map((h) => `  - ${h}`).join('\n');
                        preamble += `\n- ${labels.manageWithLabel}:\n${nestedHints}`;
                    }
                    const memMsg = {
                        type: 'transient_guide_msg',
                        role: 'assistant',
                        content: `${preamble}\n---\n${content.trim()}\n`,
                    };
                    if (scopeKind === 'shared') {
                        groupedMemories.shared.push(memMsg);
                    }
                    else {
                        groupedMemories.personal.push(memMsg);
                    }
                    memories.push(memMsg);
                }
                catch (err) {
                    log_1.log.warn(`Failed to read memory file ${file.absolutePath}:`, err);
                }
            }
        };
        const sharedScopeLabelText = (0, minds_i18n_1.sharedScopeLabel)(workingLanguage);
        const personalScopeLabelText = (0, minds_i18n_1.personalScopeLabel)(workingLanguage, agent.id);
        await readMemFiles(sharedFiles, 'shared', sharedScopeLabelText, [
            'replace_team_memory',
            'drop_team_memory',
        ]);
        await readMemFiles(personalFiles, 'personal', personalScopeLabelText, [
            'replace_personal_memory',
            'drop_personal_memory',
        ]);
    }
    catch (err) {
        log_1.log.warn('Failed to load agent memories:', err);
    }
    // Prepend grouped section headers when any memory exists
    const groupedOutput = [];
    if (groupedMemories.shared.length > 0 || groupedMemories.personal.length > 0) {
        groupedOutput.push({
            type: 'transient_guide_msg',
            role: 'assistant',
            content: (0, minds_i18n_1.memoriesSummaryTitle)(workingLanguage),
        });
        if (groupedMemories.shared.length > 0) {
            const summaryShared = groupedMemories.shared
                .filter((m) => 'content' in m)
                .map((m) => {
                const match = m.content.match(/- (?:Path|路径): `([^`]+)`/);
                const p = match ? match[1] : '';
                return (0, minds_i18n_1.memoriesSummaryLineShared)(workingLanguage, p);
            })
                .join('\n');
            groupedOutput.push({
                type: 'transient_guide_msg',
                role: 'assistant',
                content: (0, minds_i18n_1.memoriesSummarySectionShared)(workingLanguage),
            });
            groupedOutput.push({
                type: 'transient_guide_msg',
                role: 'assistant',
                content: summaryShared,
            });
        }
        if (groupedMemories.personal.length > 0) {
            const summaryPersonal = groupedMemories.personal
                .filter((m) => 'content' in m)
                .map((m) => {
                const match = m.content.match(/- (?:Path|路径): `([^`]+)`/);
                const p = match ? match[1] : '';
                return (0, minds_i18n_1.memoriesSummaryLinePersonal)(workingLanguage, p);
            })
                .join('\n');
            groupedOutput.push({
                type: 'transient_guide_msg',
                role: 'assistant',
                content: (0, minds_i18n_1.memoriesSummarySectionPersonal)(workingLanguage),
            });
            groupedOutput.push({
                type: 'transient_guide_msg',
                role: 'assistant',
                content: summaryPersonal,
            });
        }
    }
    if (groupedMemories.shared.length > 0) {
        groupedOutput.push({
            type: 'transient_guide_msg',
            role: 'assistant',
            content: (0, minds_i18n_1.sharedMemoriesHeader)(workingLanguage),
        });
        groupedOutput.push(...groupedMemories.shared);
    }
    if (groupedMemories.personal.length > 0) {
        groupedOutput.push({
            type: 'transient_guide_msg',
            role: 'assistant',
            content: (0, minds_i18n_1.personalMemoriesHeader)(workingLanguage),
        });
        groupedOutput.push(...groupedMemories.personal);
    }
    return {
        team,
        agent,
        systemPrompt,
        memories: groupedOutput.length > 0 ? groupedOutput : memories,
        agentTools,
    };
}
