"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.listRegisteredToolsetCatalog = listRegisteredToolsetCatalog;
exports.computeGlobalToolRegistryRevision = computeGlobalToolRegistryRevision;
exports.createToolAvailabilitySnapshot = createToolAvailabilitySnapshot;
const crypto_1 = require("crypto");
const time_1 = require("@longrun-ai/kernel/utils/time");
const runtime_1 = require("./apps/runtime");
const supervisor_1 = require("./mcp/supervisor");
const rtws_1 = require("./rtws");
const team_1 = require("./team");
const ctrl_1 = require("./tools/ctrl");
const registry_1 = require("./tools/registry");
const shell_tools_1 = require("./tools/shell-tools");
const skills_1 = require("./tools/skills");
const toolset_manual_1 = require("./tools/toolset-manual");
const TOOL_AVAILABILITY_PROTOCOL_VERSION = 'tool-availability.v1';
function computeRevision(payload) {
    return `sha256:${(0, crypto_1.createHash)('sha256').update(JSON.stringify(payload), 'utf8').digest('hex')}`;
}
function toolToInfo(tool, includeParameters) {
    return {
        name: tool.name,
        kind: 'func',
        description: tool.description,
        descriptionI18n: tool.descriptionI18n,
        ...(includeParameters ? { parameters: tool.parameters } : {}),
    };
}
function listRegisteredToolsetCatalog() {
    const toolsets = [];
    for (const [toolsetName, tools] of registry_1.toolsetsRegistry.entries()) {
        const meta = (0, registry_1.getToolsetMeta)(toolsetName);
        toolsets.push({
            name: toolsetName,
            source: meta?.source ?? 'dominds',
            descriptionI18n: meta?.descriptionI18n,
            tools: tools.map((tool) => toolToInfo(tool, meta?.source === 'mcp')),
        });
    }
    return toolsets;
}
function computeGlobalToolRegistryRevision() {
    return computeRevision(listRegisteredToolsetCatalog());
}
function buildRegistryLayer() {
    const toolsets = listRegisteredToolsetCatalog();
    return {
        status: 'ready',
        revision: computeRevision(toolsets),
        toolsets,
    };
}
function buildNotApplicableMemberBindingLayer() {
    const payload = {
        status: 'not_applicable',
        declaredToolsetSelectors: [],
        declaredToolIds: [],
        resolvedStaticToolsetIds: [],
        resolvedDirectToolIds: [],
        unresolvedDeclaredToolsetIds: [],
        unresolvedDeclaredToolIds: [],
    };
    return {
        ...payload,
        revision: computeRevision(payload),
    };
}
function buildMemberBindingLayer(member, mcpDeclaredToolsets) {
    const declaredToolsetSelectors = [...(member.toolsets ?? [])];
    const declaredToolIds = [...(member.tools ?? [])];
    const declaredMcpToolsetNames = mcpDeclaredToolsets.kind === 'loaded' ? mcpDeclaredToolsets.declaredServerIds : undefined;
    const invalidMcpToolsetNames = mcpDeclaredToolsets.kind === 'loaded' ? mcpDeclaredToolsets.invalidServerIds : undefined;
    const resolvedStaticToolsetIds = member.listResolvedToolsetNames({
        onMissing: 'silent',
        dynamicToolsetNames: [],
        declaredMcpToolsetNames,
        invalidMcpToolsetNames,
    });
    const resolvedDirectToolIds = [];
    const unresolvedDeclaredToolIds = [];
    for (const toolId of declaredToolIds) {
        if ((0, registry_1.getTool)(toolId)) {
            resolvedDirectToolIds.push(toolId);
        }
        else {
            unresolvedDeclaredToolIds.push(toolId);
        }
    }
    const unresolvedDeclaredToolsetIds = declaredToolsetSelectors.filter((entry) => {
        if (entry === '*' || entry.startsWith('!')) {
            return false;
        }
        return (0, registry_1.getToolset)(entry) === undefined;
    });
    const payload = {
        status: 'ready',
        memberId: member.id,
        declaredToolsetSelectors,
        declaredToolIds,
        resolvedStaticToolsetIds,
        resolvedDirectToolIds,
        unresolvedDeclaredToolsetIds,
        unresolvedDeclaredToolIds,
    };
    return {
        ...payload,
        revision: computeRevision(payload),
    };
}
function buildNotApplicableAppDynamicLayer() {
    const payload = {
        status: 'not_applicable',
        toolsetIds: [],
        unresolvedToolsetIds: [],
    };
    return {
        ...payload,
        revision: computeRevision(payload),
    };
}
async function buildAppDynamicLayer(params) {
    const result = await (0, runtime_1.resolveDynamicAppToolAvailabilityForMember)({
        rtwsRootAbs: (0, rtws_1.domindsRtwsRootAbs)(),
        memberId: params.memberId,
        taskDocPath: params.taskDocPath,
        agentId: params.agentId,
        dialogId: params.dialog?.selfId,
        mainDialogId: params.dialog?.rootId,
        sessionSlug: params.dialog?.sessionSlug,
    });
    if (result.status === 'not_applicable') {
        return buildNotApplicableAppDynamicLayer();
    }
    if (result.status === 'error') {
        const payload = {
            status: 'error',
            memberId: params.memberId,
            taskDocPath: params.taskDocPath,
            toolsetIds: [],
            unresolvedToolsetIds: [],
            errorText: result.errorText,
        };
        return {
            ...payload,
            revision: computeRevision(payload),
        };
    }
    const unresolvedToolsetIds = result.toolsetIds.filter((toolsetId) => (0, registry_1.getToolset)(toolsetId) === undefined);
    const payload = {
        status: 'ready',
        memberId: params.memberId,
        taskDocPath: params.taskDocPath,
        toolsetIds: [...result.toolsetIds],
        unresolvedToolsetIds,
    };
    return {
        ...payload,
        revision: computeRevision(payload),
    };
}
function buildRuntimeLeaseLayer(dialog) {
    if (!dialog || dialog.rootId.trim() === '' || dialog.selfId.trim() === '') {
        const payload = {
            status: 'not_applicable',
            leases: [],
        };
        return {
            ...payload,
            revision: computeRevision(payload),
        };
    }
    const normalizedRootId = dialog.rootId.trim();
    const normalizedSelfId = dialog.selfId.trim();
    const dialogKey = normalizedRootId === normalizedSelfId
        ? normalizedSelfId
        : `${normalizedRootId}#${normalizedSelfId}`;
    const leases = [...(0, supervisor_1.getMcpRuntimeLeasesForDialog)(dialogKey)];
    const payload = {
        status: 'ready',
        dialogKey,
        leases,
    };
    return {
        ...payload,
        revision: computeRevision(payload),
    };
}
function buildVisibleStandaloneTools(layer, existingToolNames, agentIsShellSpecialist) {
    if (layer.status !== 'ready') {
        return { visibleStandaloneToolIds: [], visibleStandaloneTools: [] };
    }
    const visibleStandaloneToolIds = [];
    const visibleStandaloneTools = [];
    for (const toolId of layer.resolvedDirectToolIds) {
        const tool = (0, registry_1.getTool)(toolId);
        if (!tool) {
            continue;
        }
        if (!agentIsShellSpecialist && (0, shell_tools_1.isShellToolName)(tool.name)) {
            continue;
        }
        if (existingToolNames.has(tool.name)) {
            continue;
        }
        visibleStandaloneToolIds.push(tool.name);
        visibleStandaloneTools.push(toolToInfo(tool, false));
    }
    const injectedStandaloneTools = [(0, toolset_manual_1.buildManTool)(), skills_1.readSkillTool];
    for (const tool of injectedStandaloneTools) {
        if (!visibleStandaloneToolIds.includes(tool.name) &&
            !existingToolNames.has(tool.name) &&
            (agentIsShellSpecialist || !(0, shell_tools_1.isShellToolName)(tool.name))) {
            visibleStandaloneToolIds.push(tool.name);
            visibleStandaloneTools.push(toolToInfo(tool, false));
        }
    }
    return {
        visibleStandaloneToolIds,
        visibleStandaloneTools,
    };
}
function buildIntrinsicControlToolset(dialog) {
    const tools = [
        ctrl_1.addReminderTool,
        ctrl_1.deleteReminderTool,
        ctrl_1.updateReminderTool,
        ctrl_1.clearMindTool,
        ctrl_1.recallTaskdocTool,
    ];
    const isSideDialog = dialog !== undefined && dialog.rootId !== dialog.selfId;
    if (!isSideDialog) {
        tools.push(ctrl_1.doMindTool, ctrl_1.mindMoreTool, ctrl_1.changeMindTool, ctrl_1.neverMindTool);
    }
    const meta = (0, registry_1.getToolsetMeta)('control');
    return {
        name: 'control',
        source: meta?.source ?? 'dominds',
        descriptionI18n: meta?.descriptionI18n,
        tools: tools.map((tool) => toolToInfo(tool, false)),
    };
}
function buildComposition(args) {
    const registryById = new Map(args.registry.toolsets.map((toolset) => [toolset.name, toolset]));
    const visibleToolsetIds = [];
    const seen = new Set();
    const excludedToolsetIds = new Set(args.memberBinding.declaredToolsetSelectors
        .filter((selector) => selector.startsWith('!') && selector.length > 1)
        .map((selector) => selector.slice(1)));
    if (args.memberBinding.status === 'ready') {
        for (const toolsetId of args.memberBinding.resolvedStaticToolsetIds) {
            if (seen.has(toolsetId)) {
                continue;
            }
            if (!registryById.has(toolsetId)) {
                continue;
            }
            seen.add(toolsetId);
            visibleToolsetIds.push(toolsetId);
        }
    }
    if (args.appDynamicAvailability.status === 'ready') {
        for (const toolsetId of args.appDynamicAvailability.toolsetIds) {
            if (excludedToolsetIds.has(toolsetId)) {
                continue;
            }
            if (seen.has(toolsetId)) {
                continue;
            }
            if (!registryById.has(toolsetId)) {
                continue;
            }
            seen.add(toolsetId);
            visibleToolsetIds.push(toolsetId);
        }
    }
    const intrinsicControlToolset = args.memberBinding.status === 'ready' ? buildIntrinsicControlToolset(args.dialog) : undefined;
    if (intrinsicControlToolset !== undefined && !seen.has(intrinsicControlToolset.name)) {
        seen.add(intrinsicControlToolset.name);
        visibleToolsetIds.push(intrinsicControlToolset.name);
    }
    const visibleToolsets = visibleToolsetIds
        .map((toolsetId) => toolsetId === intrinsicControlToolset?.name
        ? intrinsicControlToolset
        : registryById.get(toolsetId))
        .filter((toolset) => toolset !== undefined)
        .map((toolset) => args.agentIsShellSpecialist
        ? toolset
        : {
            ...toolset,
            tools: toolset.tools.filter((tool) => !(0, shell_tools_1.isShellToolName)(tool.name)),
        });
    const toolsetToolNames = new Set(visibleToolsets.flatMap((toolset) => toolset.tools.map((tool) => tool.name)));
    const standaloneTools = buildVisibleStandaloneTools(args.memberBinding, toolsetToolNames, args.agentIsShellSpecialist);
    const payload = {
        visibleToolsetIds,
        visibleToolsets,
        visibleStandaloneToolIds: standaloneTools.visibleStandaloneToolIds,
        visibleStandaloneTools: standaloneTools.visibleStandaloneTools,
        runtimeLeaseAffectsVisibility: false,
    };
    return {
        ...payload,
        revision: computeRevision(payload),
    };
}
async function createToolAvailabilitySnapshot(args) {
    const context = {
        ...(typeof args?.agentId === 'string' && args.agentId.trim() !== ''
            ? { agentId: args.agentId.trim() }
            : {}),
        ...(typeof args?.taskDocPath === 'string' && args.taskDocPath.trim() !== ''
            ? { taskDocPath: args.taskDocPath.trim() }
            : {}),
        ...(args?.dialog ? { dialog: args.dialog } : {}),
    };
    const registry = buildRegistryLayer();
    const team = args?.agentId ? await team_1.Team.load() : null;
    const member = args?.agentId ? (team?.getMember(args.agentId) ?? null) : null;
    const mcpDeclaredToolsets = member !== null ? await team_1.Team.readMcpDeclaredToolsets() : { kind: 'missing' };
    const agentIsShellSpecialist = team !== null &&
        member !== null &&
        (team.shellSpecialists.includes(member.id) || member.hidden === true);
    const memberBinding = member !== null
        ? buildMemberBindingLayer(member, mcpDeclaredToolsets)
        : buildNotApplicableMemberBindingLayer();
    const appDynamicAvailability = member !== null && typeof args?.taskDocPath === 'string' && args.taskDocPath.trim() !== ''
        ? await buildAppDynamicLayer({
            memberId: member.id,
            taskDocPath: args.taskDocPath.trim(),
            agentId: args.agentId?.trim(),
            dialog: args.dialog,
        })
        : buildNotApplicableAppDynamicLayer();
    const runtimeLease = buildRuntimeLeaseLayer(args?.dialog);
    const composition = buildComposition({
        registry,
        memberBinding,
        appDynamicAvailability,
        dialog: context.dialog,
        agentIsShellSpecialist,
    });
    return {
        protocolVersion: TOOL_AVAILABILITY_PROTOCOL_VERSION,
        context,
        layers: {
            registry,
            memberBinding,
            appDynamicAvailability,
            runtimeLease,
        },
        composition,
        timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
    };
}
