"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ToolsService = void 0;
const utils_1 = require("../utils");
const types_1 = require("./types");
class ToolsService {
    context = {};
    tools = [];
    overrides = [];
    wrappers = [];
    originalFunctions = {};
    functions = {};
    constructor(context) {
        if (context) {
            this.context = { ...context, Tools: this };
        }
        else {
            this.context = { Tools: this };
        }
    }
    getContext() {
        return this.context;
    }
    setContext(context) {
        this.context = { ...context, Tools: this };
    }
    addContext(key, value) {
        this.context[key] = value;
    }
    getTools() {
        return this.tools;
    }
    getToolsByNames(names) {
        return this.tools.filter((tool) => names.some((name) => name && tool.function.name.endsWith(name)));
    }
    copyToolsFrom(toolNames, toolsService) {
        const tools = toolsService.getToolsByNames(toolNames);
        this.addTools(tools);
        for (const name of toolNames) {
            const fn = toolsService.getFunction(name);
            if (!fn) {
                continue;
            }
            this.setFunction(name, fn);
        }
    }
    getToolNames() {
        return this.tools.map((tool) => tool.function.name);
    }
    getTool(name) {
        return this.tools.find((tool) => name &&
            (tool.function.name === name || tool.function.name.endsWith(name)));
    }
    getFunction(name) {
        const tool = this.getTool(name);
        const functionName = tool ? tool.function.name : name;
        if (this.functions[functionName] || this.originalFunctions[functionName]) {
            this.applyOverridesAndWrappers(functionName);
        }
        else {
            const matchingOverride = this.findMatchingOverride(functionName);
            if (matchingOverride) {
                this.functions[functionName] = matchingOverride.override;
            }
            else {
                return undefined;
            }
        }
        return this.functions[functionName];
    }
    setFunction(name, func) {
        this.originalFunctions[name] = func.bind(this);
        this.functions[name] = func.bind(this);
        this.applyOverridesAndWrappers(name);
    }
    setFunctions(names, funcs) {
        for (let i = 0; i < names.length; i++) {
            if (!names[i] || typeof funcs[i] !== "function") {
                console.warn("Ignoring invalid function entry at index", i, "with name", names[i]);
                continue;
            }
            this.setFunction(names[i], funcs[i]);
        }
    }
    addTool(tool) {
        this.tools.push(tool);
    }
    addTools(tools) {
        const existingTools = this.getToolNames();
        const filteredTools = tools.filter((tool) => !existingTools.includes(tool.function.name));
        this.tools.push(...filteredTools);
    }
    addFunctions(fns) {
        for (const fnName of Object.keys(fns)) {
            if (typeof fns[fnName] !== "function") {
                continue;
            }
            this.setFunction(fnName, fns[fnName]);
        }
    }
    defineTools(tools, functions) {
        this.addTools(tools);
        this.addFunctions(functions);
    }
    async callTool(toolCall, enabledTools = this.getToolNames()) {
        const functionName = toolCall.function.name;
        let functionArgs;
        try {
            try {
                functionArgs =
                    typeof toolCall.function.arguments === "string"
                        ? JSON.parse((0, utils_1.restoreEscapedNewLines)(toolCall.function.arguments))
                        : toolCall.function.arguments;
            }
            catch (error) {
                throw new Error(`Invalid JSON in tool call arguments: ${error.message}`);
            }
            if (!enabledTools.some((t) => t.endsWith(functionName))) {
                const options = enabledTools.join(", ");
                throw new Error(`Function ${functionName} not enabled, options are ${options}`);
            }
            const toolDefinition = this.getTool(functionName);
            if (!toolDefinition) {
                throw new Error(`Tool ${functionName} not found`);
            }
            const toolName = toolDefinition.function.name;
            const functionToCall = this.getFunction(toolName);
            if (!functionToCall) {
                const options = enabledTools.join(", ");
                throw new Error(`Function ${toolName} not found, options are ${options}`);
            }
            const properties = toolDefinition?.function?.parameters?.properties || {};
            const isPositional = toolDefinition?.function?.parameters?.positional || false;
            const fnArgs = isPositional
                ? Object.keys(properties).map((p) => functionArgs[p])
                : functionArgs;
            const rawResponse = isPositional
                ? functionToCall.call(this, ...fnArgs)
                : functionToCall.call(this, fnArgs);
            const functionResponse = await Promise.resolve(rawResponse).catch((e) => {
                throw new Error("ERROR: " + e.message);
            });
            const toJsonIfObject = (arg) => {
                if (arg === null) {
                    return "null";
                }
                if (arg === undefined) {
                    return "undefined";
                }
                if (typeof arg === "object") {
                    return JSON.stringify(arg, null, 2);
                }
                return arg;
            };
            let toolMessages = [];
            if (functionName === "multi_tool_use.parallel") {
                const toolCallsArg = Array.isArray(fnArgs) ? fnArgs : [fnArgs];
                const toolCalls = Array.isArray(toolCallsArg) ? toolCallsArg : [];
                const calls = toolCalls;
                toolMessages = calls.map((call, index) => {
                    return {
                        tool_call_id: `call_parallel_${index}`,
                        role: "tool",
                        name: call.recipient_name.split(".").pop(),
                        content: toJsonIfObject(functionResponse[index]) || "Done",
                    };
                });
            }
            else {
                toolMessages = [
                    {
                        tool_call_id: toolCall.id,
                        role: "tool",
                        name: functionName,
                        content: toJsonIfObject(functionResponse) || "Done",
                    },
                ];
            }
            return {
                toolMessages,
                toolCallId: toolCall.id,
                functionName,
                functionArgs,
                functionResp: this.unwrapMcpResponse(functionResponse) || "Done",
            };
        }
        catch (error) {
            console.log(error.message);
            const toolMessages = [
                {
                    tool_call_id: toolCall.id,
                    role: "tool",
                    name: "error",
                    content: error.message,
                },
            ];
            return {
                toolMessages,
                toolCallId: toolCall.id,
                functionName,
                functionArgs,
                functionResp: undefined,
            };
        }
    }
    registerOverride(pattern, override, priority = 0) {
        this.overrides.push({ pattern, override, priority });
        this.overrides.sort((a, b) => b.priority - a.priority);
        for (const toolName of this.getToolNames()) {
            if (this.functions[toolName]) {
                this.applyOverridesAndWrappers(toolName);
            }
        }
    }
    registerWrapper(pattern, wrapper, priority = 0) {
        this.wrappers.push({ pattern, wrapper, priority });
        this.wrappers.sort((a, b) => b.priority - a.priority);
        for (const toolName of this.getToolNames()) {
            if (this.functions[toolName]) {
                this.applyOverridesAndWrappers(toolName);
            }
        }
    }
    removeOverride(pattern) {
        this.overrides = this.overrides.filter((reg) => reg.pattern !== pattern);
        for (const toolName of this.getToolNames()) {
            if (this.originalFunctions[toolName]) {
                this.setFunction(toolName, this.originalFunctions[toolName]);
            }
        }
    }
    removeWrapper(pattern) {
        this.wrappers = this.wrappers.filter((reg) => reg.pattern !== pattern);
        for (const toolName of this.getToolNames()) {
            if (this.originalFunctions[toolName]) {
                this.setFunction(toolName, this.originalFunctions[toolName]);
            }
        }
    }
    applyOverridesAndWrappers(name) {
        if (!this.originalFunctions[name]) {
            this.originalFunctions[name] = this.functions[name];
        }
        const originalFunc = this.originalFunctions[name];
        const matchingOverride = this.findMatchingOverride(name);
        if (matchingOverride) {
            this.functions[name] = ((...args) => {
                const toolDefinition = this.getTool(name);
                return matchingOverride.override.call(this, args, toolDefinition);
            }).bind(this);
            return;
        }
        const wrappers = this.findMatchingWrappers(name);
        if (wrappers.length > 0) {
            let wrappedFunction = originalFunc;
            for (const wrapperReg of wrappers) {
                const innerFunc = wrappedFunction;
                wrappedFunction = ((...args) => {
                    const toolDefinition = this.getTool(name);
                    return wrapperReg.wrapper(innerFunc, args, toolDefinition);
                }).bind(this);
            }
            this.functions[name] = wrappedFunction.bind(this);
        }
        else {
            this.functions[name] = originalFunc;
        }
    }
    findMatchingOverride(toolName) {
        let bestMatch = null;
        let highestPriority = -1;
        for (const registration of this.overrides) {
            const matcher = (0, types_1.createPatternMatcher)(registration.pattern);
            if (matcher.matches(toolName)) {
                if (registration.priority > highestPriority) {
                    highestPriority = registration.priority;
                    bestMatch = registration;
                }
            }
        }
        return bestMatch;
    }
    findMatchingWrappers(toolName) {
        const matchingWrappers = [];
        for (const registration of this.wrappers) {
            const matcher = (0, types_1.createPatternMatcher)(registration.pattern);
            if (matcher.matches(toolName)) {
                matchingWrappers.push(registration);
            }
        }
        return matchingWrappers;
    }
    tryParseJson(value) {
        try {
            return JSON.parse(value);
        }
        catch {
            return null;
        }
    }
    unwrapMcpResponse(response) {
        if (response === null || typeof response !== "object") {
            return response;
        }
        const { content } = response;
        if (!Array.isArray(content) || content.length === 0) {
            return response;
        }
        const firstItem = content[0];
        if (!firstItem ||
            firstItem.type !== "text" ||
            typeof firstItem.text !== "string") {
            return response;
        }
        const parsed = this.tryParseJson(firstItem.text);
        if (parsed === null) {
            return firstItem.text;
        }
        return this.unwrapMcpResponse(parsed);
    }
    getOriginalFunction(name) {
        return this.originalFunctions[name];
    }
    clearOverrides() {
        this.overrides = [];
        this.wrappers = [];
        for (const [name, originalFunc] of Object.entries(this.originalFunctions)) {
            this.functions[name] = originalFunc;
        }
    }
}
exports.ToolsService = ToolsService;
//# sourceMappingURL=Tools.js.map