"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Tools_1 = require("../../src/services/Tools");
const types_1 = require("../../src/services/types");
describe("ToolsService", () => {
    let toolsService;
    beforeEach(() => {
        toolsService = new Tools_1.ToolsService();
    });
    describe("constructor", () => {
        it("should initialize with empty context when no context is provided", () => {
            const service = new Tools_1.ToolsService();
            const context = service.getContext();
            expect(context).toBeDefined();
            expect(context.Tools).toBe(service);
            expect(context.Agents).toBeUndefined();
            expect(context.Events).toBeUndefined();
            expect(context.Clients).toBeUndefined();
            expect(context.Plugins).toBeUndefined();
        });
        it("should initialize with provided context", () => {
            const mockAgents = {};
            const mockEvents = {};
            const initialContext = {
                Agents: mockAgents,
                Events: mockEvents,
            };
            const service = new Tools_1.ToolsService(initialContext);
            const context = service.getContext();
            expect(context.Tools).toBe(service);
            expect(context.Agents).toBe(mockAgents);
            expect(context.Events).toBe(mockEvents);
        });
    });
    describe("context management", () => {
        it("should set and get context", () => {
            const mockContext = {
                Agents: {},
                metadata: { test: "value" },
            };
            toolsService.setContext(mockContext);
            const context = toolsService.getContext();
            expect(context.Tools).toBe(toolsService);
            expect(context.Agents).toBe(mockContext.Agents);
            expect(context.metadata).toEqual({ test: "value" });
        });
        it("should add context properties", () => {
            const mockAgents = {};
            toolsService.addContext("Agents", mockAgents);
            const context = toolsService.getContext();
            expect(context.Agents).toBe(mockAgents);
            expect(context.Tools).toBe(toolsService);
        });
    });
    describe("tool management", () => {
        const mockTool = {
            type: "function",
            function: {
                name: "testTool",
                description: "A test tool",
                parameters: {
                    type: "object",
                    properties: {
                        input: { type: "string", description: "Test input" },
                    },
                    required: ["input"],
                },
            },
        };
        it("should add a single tool", () => {
            toolsService.addTool(mockTool);
            expect(toolsService.getTools()).toContain(mockTool);
            expect(toolsService.getToolNames()).toContain("testTool");
        });
        it("should add multiple tools", () => {
            const tool2 = {
                type: "function",
                function: {
                    name: "testTool2",
                    description: "Another test tool",
                    parameters: {
                        type: "object",
                        properties: {},
                        required: [],
                    },
                },
            };
            toolsService.addTools([mockTool, tool2]);
            expect(toolsService.getTools()).toHaveLength(2);
            expect(toolsService.getToolNames()).toEqual(["testTool", "testTool2"]);
        });
        it("should prevent duplicate tool names", () => {
            toolsService.addTool(mockTool);
            toolsService.addTools([mockTool]);
            expect(toolsService.getTools()).toHaveLength(1);
        });
        it("should get tool by name", () => {
            toolsService.addTool(mockTool);
            const retrievedTool = toolsService.getTool("testTool");
            expect(retrievedTool).toBe(mockTool);
            const nonExistentTool = toolsService.getTool("nonExistent");
            expect(nonExistentTool).toBeUndefined();
        });
        it("should get tools by names", () => {
            const tool2 = {
                type: "function",
                function: {
                    name: "testTool2",
                    description: "Another test tool",
                    parameters: {
                        type: "object",
                        properties: {},
                        required: [],
                    },
                },
            };
            toolsService.addTools([mockTool, tool2]);
            const selectedTools = toolsService.getToolsByNames(["testTool"]);
            expect(selectedTools).toHaveLength(1);
            expect(selectedTools[0]).toBe(mockTool);
        });
    });
    describe("function management", () => {
        it("should set and get functions", () => {
            const testFunction = jest.fn().mockReturnValue("test result");
            toolsService.setFunction("testFunc", testFunction);
            const retrievedFunction = toolsService.getFunction("testFunc");
            expect(retrievedFunction).toBeDefined();
            expect(typeof retrievedFunction).toBe("function");
        });
        it("should add multiple functions", () => {
            const functions = {
                func1: jest.fn(),
                func2: jest.fn(),
            };
            toolsService.addFunctions(functions);
            expect(toolsService.getFunction("func1")).toBeDefined();
            expect(toolsService.getFunction("func2")).toBeDefined();
        });
        it("should set multiple functions by names and functions arrays", () => {
            const func1 = jest.fn();
            const func2 = jest.fn();
            toolsService.setFunctions(["test1", "test2"], [func1, func2]);
            expect(toolsService.getFunction("test1")).toBeDefined();
            expect(toolsService.getFunction("test2")).toBeDefined();
        });
        it("should define tools and functions together", () => {
            const mockTool = {
                type: "function",
                function: {
                    name: "combinedTool",
                    parameters: {
                        type: "object",
                        properties: {},
                        required: [],
                    },
                },
            };
            const mockFunction = jest.fn();
            toolsService.defineTools([mockTool], { combinedTool: mockFunction });
            expect(toolsService.getTool("combinedTool")).toBe(mockTool);
            expect(toolsService.getFunction("combinedTool")).toBeDefined();
        });
    });
    describe("copyToolsFrom", () => {
        it("should copy tools and functions from another ToolsService", () => {
            const sourceService = new Tools_1.ToolsService();
            const mockTool = {
                type: "function",
                function: {
                    name: "sourceTool",
                    parameters: {
                        type: "object",
                        properties: {},
                        required: [],
                    },
                },
            };
            const mockFunction = jest.fn();
            sourceService.addTool(mockTool);
            sourceService.setFunction("sourceTool", mockFunction);
            toolsService.copyToolsFrom(["sourceTool"], sourceService);
            expect(toolsService.getTool("sourceTool")).toEqual(mockTool);
            expect(toolsService.getFunction("sourceTool")).toBeDefined();
        });
    });
    describe("callTool", () => {
        const mockTool = {
            type: "function",
            function: {
                name: "testTool",
                description: "A test tool",
                parameters: {
                    type: "object",
                    properties: {
                        input: { type: "string", description: "Test input" },
                        optional: { type: "string", description: "Optional input" },
                    },
                    required: ["input"],
                },
            },
        };
        const mockToolCall = {
            id: "call_123",
            type: "function",
            function: {
                name: "testTool",
                arguments: JSON.stringify({ input: "test value" }),
            },
        };
        beforeEach(() => {
            toolsService.addTool(mockTool);
            jest.clearAllMocks();
        });
        it("should successfully call a tool with correct arguments", async () => {
            const mockFunction = jest.fn().mockResolvedValue("success result");
            toolsService.setFunction("testTool", mockFunction);
            const result = await toolsService.callTool(mockToolCall);
            expect(mockFunction).toHaveBeenCalledWith({ input: "test value" });
            expect(result.toolMessages).toHaveLength(1);
            expect(result.toolMessages[0]).toEqual({
                tool_call_id: "call_123",
                role: "tool",
                name: "testTool",
                content: "success result",
            });
            expect(result.functionResp).toBe("success result");
        });
        it("should handle positional arguments", async () => {
            const positionalTool = {
                type: "function",
                function: {
                    name: "positionalTool",
                    parameters: {
                        type: "object",
                        positional: true,
                        properties: {
                            arg1: { type: "string" },
                            arg2: { type: "number" },
                        },
                        required: ["arg1", "arg2"],
                    },
                },
            };
            const positionalCall = {
                id: "call_456",
                type: "function",
                function: {
                    name: "positionalTool",
                    arguments: JSON.stringify({ arg1: "hello", arg2: 42 }),
                },
            };
            const mockFunction = jest.fn().mockResolvedValue("positional result");
            toolsService.addTool(positionalTool);
            toolsService.setFunction("positionalTool", mockFunction);
            await toolsService.callTool(positionalCall);
            expect(mockFunction).toHaveBeenCalledWith("hello", 42);
        });
        it("should handle tool not enabled error", async () => {
            const mockFunction = jest.fn();
            toolsService.setFunction("testTool", mockFunction);
            const result = await toolsService.callTool(mockToolCall, ["otherTool"]);
            expect(mockFunction).not.toHaveBeenCalled();
            expect(result.toolMessages[0].name).toBe("error");
            expect(result.toolMessages[0].content).toContain("not enabled");
            expect(result.functionResp).toBeUndefined();
        });
        it("should handle tool definition not found", async () => {
            const unknownToolCall = {
                id: "call_unknown",
                type: "function",
                function: {
                    name: "unknownTool",
                    arguments: "{}",
                },
            };
            const result = await toolsService.callTool(unknownToolCall);
            expect(result.toolMessages[0].name).toBe("error");
            expect(result.toolMessages[0].content).toContain("not enabled");
        });
        it("should handle function implementation not found", async () => {
            const result = await toolsService.callTool(mockToolCall);
            expect(result.toolMessages[0].name).toBe("error");
            expect(result.toolMessages[0].content).toContain("not found");
        });
        it("should handle function execution errors", async () => {
            const mockFunction = jest
                .fn()
                .mockRejectedValue(new Error("Function failed"));
            toolsService.setFunction("testTool", mockFunction);
            const result = await toolsService.callTool(mockToolCall);
            expect(result.toolMessages[0].name).toBe("error");
            expect(result.toolMessages[0].content).toContain("ERROR: Function failed");
            expect(result.functionResp).toBeUndefined();
        });
        it("should handle object responses by converting to JSON", async () => {
            const mockFunction = jest
                .fn()
                .mockResolvedValue({ key: "value", nested: { data: 123 } });
            toolsService.setFunction("testTool", mockFunction);
            const result = await toolsService.callTool(mockToolCall);
            expect(result.toolMessages[0].content).toBe(JSON.stringify({ key: "value", nested: { data: 123 } }, null, 2));
        });
        it("should handle multi_tool_use.parallel special case", async () => {
            const parallelTool = {
                type: "function",
                function: {
                    name: "multi_tool_use.parallel",
                    parameters: {
                        type: "object",
                        properties: {},
                        required: [],
                    },
                },
            };
            const parallelCall = {
                id: "call_parallel",
                type: "function",
                function: {
                    name: "multi_tool_use.parallel",
                    arguments: JSON.stringify([
                        { recipient_name: "tool1.action", parameters: {} },
                        { recipient_name: "tool2.action", parameters: {} },
                    ]),
                },
            };
            const mockFunction = jest.fn().mockResolvedValue(["result1", "result2"]);
            toolsService.addTool(parallelTool);
            toolsService.setFunction("multi_tool_use.parallel", mockFunction);
            const result = await toolsService.callTool(parallelCall);
            expect(result.toolMessages).toHaveLength(2);
            expect(result.toolMessages[0].tool_call_id).toBe("call_parallel_0");
            expect(result.toolMessages[1].tool_call_id).toBe("call_parallel_1");
            expect(result.toolMessages[0].name).toBe("action");
            expect(result.toolMessages[1].name).toBe("action");
        });
        it("should handle string arguments that need parsing", async () => {
            const toolCallWithStringArgs = {
                id: "call_string",
                type: "function",
                function: {
                    name: "testTool",
                    arguments: '{"input": "escaped\\nstring"}',
                },
            };
            const mockFunction = jest.fn().mockResolvedValue("success");
            toolsService.setFunction("testTool", mockFunction);
            await toolsService.callTool(toolCallWithStringArgs);
            expect(mockFunction).toHaveBeenCalledWith({ input: "escaped\nstring" });
        });
    });
    describe("Tool Override System", () => {
        const mockTool = {
            type: "function",
            function: {
                name: "overridableTool",
                description: "A tool that can be overridden",
                parameters: {
                    type: "object",
                    properties: {
                        input: { type: "string" },
                    },
                    required: ["input"],
                },
            },
        };
        beforeEach(() => {
            toolsService.addTool(mockTool);
            jest.clearAllMocks();
        });
        it("should register override with string pattern", () => {
            const overrideFunction = jest.fn().mockResolvedValue("override result");
            toolsService.registerOverride("overridableTool", overrideFunction);
            expect(() => toolsService.getFunction("overridableTool")).not.toThrow();
        });
        it("should register override with regex pattern", () => {
            const overrideFunction = jest.fn().mockResolvedValue("regex override");
            const pattern = /^overridable/;
            toolsService.registerOverride(pattern, overrideFunction);
            expect(() => toolsService.getFunction("overridableTool")).not.toThrow();
        });
        it("should execute override function instead of original", async () => {
            const originalFunction = jest.fn().mockResolvedValue("original result");
            const overrideFunction = jest.fn().mockResolvedValue("override result");
            toolsService.setFunction("overridableTool", originalFunction);
            toolsService.registerOverride("overridableTool", overrideFunction);
            const toolCall = {
                id: "call_override",
                type: "function",
                function: {
                    name: "overridableTool",
                    arguments: JSON.stringify({ input: "test" }),
                },
            };
            const result = await toolsService.callTool(toolCall);
            expect(overrideFunction).toHaveBeenCalledWith([{ input: "test" }], expect.any(Object));
            expect(originalFunction).not.toHaveBeenCalled();
            expect(result.functionResp).toBe("override result");
        });
        it("should handle override priority ordering", async () => {
            const override1 = jest.fn().mockResolvedValue("override1");
            const override2 = jest.fn().mockResolvedValue("override2");
            toolsService.registerOverride("overridableTool", override1, 1);
            toolsService.registerOverride("overridableTool", override2, 2);
            const func = toolsService.getFunction("overridableTool");
            expect(await func({})).toBe("override2");
        });
        it("should handle pattern matching with wildcards", async () => {
            const overrideFunction = jest.fn().mockResolvedValue("wildcard override");
            toolsService.registerOverride("overrid*Tool", overrideFunction);
            const func = toolsService.getFunction("overridableTool");
            expect(await func({})).toBe("wildcard override");
        });
        it("should remove overrides", async () => {
            const originalFunction = jest.fn().mockResolvedValue("original");
            const overrideFunction = jest.fn().mockResolvedValue("override");
            toolsService.setFunction("overridableTool", originalFunction);
            toolsService.registerOverride("overridableTool", overrideFunction);
            const overriddenFunc = toolsService.getFunction("overridableTool");
            expect(await overriddenFunc({})).toBe("override");
            toolsService.removeOverride("overridableTool");
            const restoredFunc = toolsService.getFunction("overridableTool");
            expect(await restoredFunc({})).toBe("original");
        });
        it("should preserve original functions when override is registered", async () => {
            const originalFunction = jest.fn().mockResolvedValue("original");
            const overrideFunction = jest.fn().mockResolvedValue("override");
            toolsService.setFunction("overridableTool", originalFunction);
            toolsService.registerOverride("overridableTool", overrideFunction);
            const originalStored = toolsService.getOriginalFunction("overridableTool");
            expect(await originalStored({})).toBe("original");
        });
    });
    describe("Tool Wrapper System", () => {
        const mockTool = {
            type: "function",
            function: {
                name: "wrappableTool",
                description: "A tool that can be wrapped",
                parameters: {
                    type: "object",
                    properties: {
                        input: { type: "string" },
                    },
                    required: ["input"],
                },
            },
        };
        beforeEach(() => {
            toolsService.addTool(mockTool);
            jest.clearAllMocks();
        });
        it("should register wrapper with string pattern", () => {
            const wrapperFunction = jest
                .fn()
                .mockImplementation((originalFn, args) => {
                return originalFn(args);
            });
            toolsService.registerWrapper("wrappableTool", wrapperFunction);
            expect(() => toolsService.getFunction("wrappableTool")).not.toThrow();
        });
        it("should register wrapper with regex pattern", () => {
            const wrapperFunction = jest
                .fn()
                .mockImplementation((originalFn, args) => {
                return originalFn(args);
            });
            const pattern = /^wrappable/;
            toolsService.registerWrapper(pattern, wrapperFunction);
            expect(() => toolsService.getFunction("wrappableTool")).not.toThrow();
        });
        it("should execute wrapper function with original function", async () => {
            const originalFunction = jest.fn().mockResolvedValue("original result");
            const wrapperFunction = jest
                .fn()
                .mockImplementation(async (originalFn, args, tool) => {
                const result = await originalFn(...args);
                return `wrapped: ${result}`;
            });
            toolsService.setFunction("wrappableTool", originalFunction);
            toolsService.registerWrapper("wrappableTool", wrapperFunction);
            const toolCall = {
                id: "call_wrapper",
                type: "function",
                function: {
                    name: "wrappableTool",
                    arguments: JSON.stringify({ input: "test" }),
                },
            };
            const result = await toolsService.callTool(toolCall);
            expect(wrapperFunction).toHaveBeenCalled();
            expect(originalFunction).toHaveBeenCalledWith({ input: "test" });
            expect(result.functionResp).toBe("wrapped: original result");
        });
        it("should handle wrapper priority ordering", () => {
            const originalFunction = jest.fn().mockResolvedValue("original");
            const wrapper1 = jest.fn().mockImplementation((fn, args) => fn(args));
            const wrapper2 = jest.fn().mockImplementation((fn, args) => fn(args));
            toolsService.setFunction("wrappableTool", originalFunction);
            toolsService.registerWrapper("wrappableTool", wrapper1, 1);
            toolsService.registerWrapper("wrappableTool", wrapper2, 2);
            const func = toolsService.getFunction("wrappableTool");
            expect(func).not.toBe(originalFunction);
            expect(func).not.toBe(wrapper1);
        });
        it("should support multiple wrapper chaining", async () => {
            const originalFunction = jest.fn().mockResolvedValue("original");
            const wrapper1 = jest
                .fn()
                .mockImplementation(async (originalFn, args) => {
                const result = await originalFn(args);
                return `wrapper1(${result})`;
            });
            const wrapper2 = jest
                .fn()
                .mockImplementation(async (originalFn, args) => {
                const result = await originalFn(args);
                return `wrapper2(${result})`;
            });
            toolsService.setFunction("wrappableTool", originalFunction);
            toolsService.registerWrapper("wrappableTool", wrapper1, 1);
            toolsService.registerWrapper("wrappableTool", wrapper2, 2);
            const toolCall = {
                id: "call_chained",
                type: "function",
                function: {
                    name: "wrappableTool",
                    arguments: JSON.stringify({ input: "test" }),
                },
            };
            const result = await toolsService.callTool(toolCall);
            expect(result.functionResp).toContain("wrapper1");
            expect(result.functionResp).toContain("wrapper2");
            expect(originalFunction).toHaveBeenCalled();
        });
        it("should remove wrappers", async () => {
            const originalFunction = jest.fn().mockResolvedValue("original");
            const wrapperFunction = jest
                .fn()
                .mockImplementation((fn, args) => fn(args));
            toolsService.setFunction("wrappableTool", originalFunction);
            toolsService.registerWrapper("wrappableTool", wrapperFunction);
            expect(toolsService.getFunction("wrappableTool")).not.toBe(originalFunction);
            toolsService.removeWrapper("wrappableTool");
            const restoredFunc = toolsService.getFunction("wrappableTool");
            expect(await restoredFunc({})).toBe("original");
        });
    });
    describe("Pattern Matching", () => {
        it("should match glob patterns with wildcards", async () => {
            const originalFunction = jest.fn().mockResolvedValue("original");
            const overrideFunction = jest.fn().mockResolvedValue("override");
            toolsService.setFunction("testTool", originalFunction);
            toolsService.setFunction("testHelper", originalFunction);
            toolsService.setFunction("otherTool", originalFunction);
            toolsService.registerOverride("test*", overrideFunction);
            const testFunc = toolsService.getFunction("testTool");
            const testHelperFunc = toolsService.getFunction("testHelper");
            const otherFunc = toolsService.getFunction("otherTool");
            expect(await testFunc({})).toBe("override");
            expect(await testHelperFunc({})).toBe("override");
            expect(await otherFunc({})).toBe("original");
        });
        it("should match regex patterns", async () => {
            const originalFunction = jest.fn().mockResolvedValue("original");
            const overrideFunction = jest.fn().mockResolvedValue("override");
            toolsService.setFunction("tool123", originalFunction);
            toolsService.setFunction("tool456", originalFunction);
            toolsService.setFunction("toolABC", originalFunction);
            toolsService.registerOverride(/tool\d+$/, overrideFunction);
            const tool123Func = toolsService.getFunction("tool123");
            const tool456Func = toolsService.getFunction("tool456");
            const toolABCFunc = toolsService.getFunction("toolABC");
            expect(await tool123Func({})).toBe("override");
            expect(await tool456Func({})).toBe("override");
            expect(await toolABCFunc({})).toBe("original");
        });
        it("should handle complex glob patterns", async () => {
            const originalFunction = jest.fn().mockResolvedValue("original");
            const overrideFunction = jest.fn().mockResolvedValue("override");
            toolsService.setFunction("api_get_user", originalFunction);
            toolsService.setFunction("api_post_user", originalFunction);
            toolsService.setFunction("api_delete_user", originalFunction);
            toolsService.setFunction("util_format", originalFunction);
            toolsService.registerOverride("api_*_user", overrideFunction);
            const apiGetFunc = toolsService.getFunction("api_get_user");
            const apiPostFunc = toolsService.getFunction("api_post_user");
            const apiDeleteFunc = toolsService.getFunction("api_delete_user");
            expect(await apiGetFunc({})).toBe("override");
            expect(await apiPostFunc({})).toBe("override");
            expect(await apiDeleteFunc({})).toBe("override");
            const utilFormatFunc = toolsService.getFunction("util_format");
            expect(await utilFormatFunc({})).toBe("original");
        });
        it("should test pattern matcher factory function directly", () => {
            const stringMatcher = (0, types_1.createPatternMatcher)("test*");
            expect(stringMatcher.matches("testTool")).toBe(true);
            expect(stringMatcher.matches("otherTool")).toBe(false);
            const regexMatcher = (0, types_1.createPatternMatcher)(/^api_/);
            expect(regexMatcher.matches("api_call")).toBe(true);
            expect(regexMatcher.matches("util_call")).toBe(false);
        });
    });
    describe("Error Handling and Edge Cases", () => {
        it("should handle malformed tool call arguments", async () => {
            const mockTool = {
                type: "function",
                function: {
                    name: "testTool",
                    parameters: { type: "object", properties: {}, required: [] },
                },
            };
            const malformedCall = {
                id: "call_malformed",
                type: "function",
                function: {
                    name: "testTool",
                    arguments: "invalid json {",
                },
            };
            toolsService.addTool(mockTool);
            toolsService.setFunction("testTool", jest.fn().mockResolvedValue("result"));
            const result = await toolsService.callTool(malformedCall);
            expect(result.toolMessages[0].name).toBe("error");
            expect(result.toolMessages[0].content).toContain("JSON");
        });
        it("should handle tools with no parameters", async () => {
            const noParamTool = {
                type: "function",
                function: {
                    name: "noParamTool",
                    description: "Tool with no parameters",
                    parameters: {
                        type: "object",
                        properties: {},
                        required: [],
                    },
                },
            };
            const noParamCall = {
                id: "call_no_param",
                type: "function",
                function: {
                    name: "noParamTool",
                    arguments: "{}",
                },
            };
            const mockFunction = jest.fn().mockResolvedValue("no param result");
            toolsService.addTool(noParamTool);
            toolsService.setFunction("noParamTool", mockFunction);
            const result = await toolsService.callTool(noParamCall);
            expect(mockFunction).toHaveBeenCalledWith({});
            expect(result.functionResp).toBe("no param result");
        });
        it("should handle tools with complex nested parameter structures", async () => {
            const complexTool = {
                type: "function",
                function: {
                    name: "complexTool",
                    parameters: {
                        type: "object",
                        properties: {
                            config: {
                                type: "object",
                                properties: {
                                    settings: {
                                        type: "array",
                                        items: {
                                            type: "object",
                                            properties: {
                                                key: { type: "string" },
                                                value: { type: "number" },
                                            },
                                        },
                                    },
                                },
                            },
                        },
                        required: ["config"],
                    },
                },
            };
            const complexCall = {
                id: "call_complex",
                type: "function",
                function: {
                    name: "complexTool",
                    arguments: JSON.stringify({
                        config: {
                            settings: [
                                { key: "timeout", value: 5000 },
                                { key: "retries", value: 3 },
                            ],
                        },
                    }),
                },
            };
            const mockFunction = jest.fn().mockResolvedValue("complex result");
            toolsService.addTool(complexTool);
            toolsService.setFunction("complexTool", mockFunction);
            const result = await toolsService.callTool(complexCall);
            expect(mockFunction).toHaveBeenCalledWith({
                config: {
                    settings: [
                        { key: "timeout", value: 5000 },
                        { key: "retries", value: 3 },
                    ],
                },
            });
            expect(result.functionResp).toBe("complex result");
        });
        it("should handle concurrent tool calls", async () => {
            const concurrentTool = {
                type: "function",
                function: {
                    name: "concurrentTool",
                    parameters: {
                        type: "object",
                        properties: {
                            delay: { type: "number" },
                        },
                        required: ["delay"],
                    },
                },
            };
            const mockFunction = jest.fn().mockImplementation(async ({ delay }) => {
                await new Promise((resolve) => setTimeout(resolve, delay));
                return `completed after ${delay}ms`;
            });
            toolsService.addTool(concurrentTool);
            toolsService.setFunction("concurrentTool", mockFunction);
            const call1 = {
                id: "call_1",
                type: "function",
                function: {
                    name: "concurrentTool",
                    arguments: JSON.stringify({ delay: 100 }),
                },
            };
            const call2 = {
                id: "call_2",
                type: "function",
                function: {
                    name: "concurrentTool",
                    arguments: JSON.stringify({ delay: 50 }),
                },
            };
            const [result1, result2] = await Promise.all([
                toolsService.callTool(call1),
                toolsService.callTool(call2),
            ]);
            expect(result1.functionResp).toBe("completed after 100ms");
            expect(result2.functionResp).toBe("completed after 50ms");
            expect(mockFunction).toHaveBeenCalledTimes(2);
        });
        it("should handle undefined and null function responses", async () => {
            const undefinedTool = {
                type: "function",
                function: {
                    name: "undefinedTool",
                    parameters: { type: "object", properties: {}, required: [] },
                },
            };
            const undefinedCall = {
                id: "call_undefined",
                type: "function",
                function: {
                    name: "undefinedTool",
                    arguments: "{}",
                },
            };
            toolsService.addTool(undefinedTool);
            toolsService.setFunction("undefinedTool", jest.fn().mockResolvedValue(undefined));
            let result = await toolsService.callTool(undefinedCall);
            expect(result.toolMessages[0].content).toBe("undefined");
            const nullTool = {
                type: "function",
                function: {
                    name: "nullTool",
                    parameters: { type: "object", properties: {}, required: [] },
                },
            };
            const nullCall = {
                id: "call_null",
                type: "function",
                function: {
                    name: "nullTool",
                    arguments: "{}",
                },
            };
            toolsService.addTool(nullTool);
            toolsService.setFunction("nullTool", jest.fn().mockResolvedValue(null));
            result = await toolsService.callTool(nullCall);
            expect(result.toolMessages[0].content).toBe("null");
        });
    });
    describe("Integration Tests", () => {
        it("should handle complete workflow with override and wrapper", async () => {
            const baseTool = {
                type: "function",
                function: {
                    name: "workflowTool",
                    parameters: {
                        type: "object",
                        properties: {
                            input: { type: "string" },
                        },
                        required: ["input"],
                    },
                },
            };
            const originalFunction = jest.fn().mockResolvedValue("original");
            const wrapperFunction = jest
                .fn()
                .mockImplementation(async (originalFn, args) => {
                const result = await originalFn(args);
                return `wrapped: ${result}`;
            });
            const overrideFunction = jest.fn().mockResolvedValue("override");
            toolsService.addTool(baseTool);
            toolsService.setFunction("workflowTool", originalFunction);
            toolsService.registerWrapper("workflowTool", wrapperFunction);
            toolsService.registerOverride("workflowTool", overrideFunction);
            const toolCall = {
                id: "call_workflow",
                type: "function",
                function: {
                    name: "workflowTool",
                    arguments: JSON.stringify({ input: "test" }),
                },
            };
            const result = await toolsService.callTool(toolCall);
            expect(overrideFunction).toHaveBeenCalled();
            expect(wrapperFunction).not.toHaveBeenCalled();
            expect(originalFunction).not.toHaveBeenCalled();
            expect(result.functionResp).toBe("override");
        });
        it("should maintain tool state across multiple operations", () => {
            const tools = [
                {
                    type: "function",
                    function: {
                        name: "tool1",
                        parameters: { type: "object", properties: {}, required: [] },
                    },
                },
                {
                    type: "function",
                    function: {
                        name: "tool2",
                        parameters: { type: "object", properties: {}, required: [] },
                    },
                },
            ];
            toolsService.addTools(tools);
            expect(toolsService.getToolsByNames(["tool1", "tool2"])).toHaveLength(2);
            toolsService.setFunction("tool1", jest.fn());
            toolsService.setFunction("tool2", jest.fn());
            expect(toolsService.getFunction("tool1")).toBeDefined();
            expect(toolsService.getFunction("tool2")).toBeDefined();
            const newService = new Tools_1.ToolsService();
            newService.copyToolsFrom(["tool1", "tool2"], toolsService);
            expect(newService.getToolsByNames(["tool1", "tool2"])).toHaveLength(2);
        });
        describe("Function Binding", () => {
            let toolsService;
            let mockContext;
            beforeEach(() => {
                mockContext = {
                    metadata: { testKey: "testValue" },
                };
                toolsService = new Tools_1.ToolsService(mockContext);
            });
            describe("this binding in tool functions", () => {
                it("should bind functions so 'this' refers to ToolsService instance", async () => {
                    const testTool = {
                        type: "function",
                        function: {
                            name: "testThisBinding",
                            description: "Test function that accesses this",
                            parameters: {
                                type: "object",
                                properties: {
                                    message: { type: "string" },
                                },
                            },
                        },
                    };
                    function testFunction(args) {
                        if (!this || typeof this.getContext !== "function") {
                            throw new Error("'this' is not bound to ToolsService instance");
                        }
                        const context = this.getContext();
                        return {
                            message: args.message,
                            contextMetadata: context.metadata,
                            hasThisBinding: true,
                        };
                    }
                    toolsService.addTool(testTool);
                    toolsService.setFunction("testThisBinding", testFunction);
                    const toolCall = {
                        id: "test-call-1",
                        type: "function",
                        function: {
                            name: "testThisBinding",
                            arguments: JSON.stringify({ message: "test" }),
                        },
                    };
                    const result = await toolsService.callTool(toolCall);
                    expect(result.functionResp.hasThisBinding).toBe(true);
                    expect(result.functionResp.contextMetadata).toEqual({
                        testKey: "testValue",
                    });
                    expect(result.functionResp.message).toBe("test");
                });
                it("should allow tool functions to call other tools via this.callTool", async () => {
                    const helperTool = {
                        type: "function",
                        function: {
                            name: "helperTool",
                            description: "Helper tool",
                            parameters: {
                                type: "object",
                                properties: {
                                    value: { type: "string" },
                                },
                            },
                        },
                    };
                    const mainTool = {
                        type: "function",
                        function: {
                            name: "mainTool",
                            description: "Main tool that calls helper",
                            parameters: {
                                type: "object",
                                properties: {
                                    input: { type: "string" },
                                },
                            },
                        },
                    };
                    function helperFunction(args) {
                        return `Helper processed: ${args.value}`;
                    }
                    async function mainFunction(args) {
                        if (!this || typeof this.callTool !== "function") {
                            throw new Error("'this' is not bound to ToolsService instance");
                        }
                        const helperCall = {
                            id: "helper-call",
                            type: "function",
                            function: {
                                name: "helperTool",
                                arguments: JSON.stringify({ value: args.input }),
                            },
                        };
                        const helperResult = await this.callTool(helperCall);
                        return `Main tool result: ${helperResult.functionResp}`;
                    }
                    toolsService.addTool(helperTool);
                    toolsService.addTool(mainTool);
                    toolsService.setFunction("helperTool", helperFunction);
                    toolsService.setFunction("mainTool", mainFunction);
                    const toolCall = {
                        id: "test-call-2",
                        type: "function",
                        function: {
                            name: "mainTool",
                            arguments: JSON.stringify({ input: "test data" }),
                        },
                    };
                    const result = await toolsService.callTool(toolCall);
                    expect(result.functionResp).toBe("Main tool result: Helper processed: test data");
                });
                it("should maintain binding through overrides", async () => {
                    const testTool = {
                        type: "function",
                        function: {
                            name: "bindingTestTool",
                            description: "Test tool for binding with overrides",
                            parameters: {
                                type: "object",
                                properties: {
                                    data: { type: "string" },
                                },
                            },
                        },
                    };
                    function originalFunction(args) {
                        if (!this || typeof this.getContext !== "function") {
                            throw new Error("'this' is not bound to ToolsService instance in original");
                        }
                        return `Original: ${args.data}`;
                    }
                    const overrideFunction = function (originalArgs, originalTool) {
                        if (!this || typeof this.getContext !== "function") {
                            throw new Error("'this' is not bound to ToolsService instance in override");
                        }
                        return `Override: ${originalArgs[0].data}`;
                    };
                    toolsService.addTool(testTool);
                    toolsService.setFunction("bindingTestTool", originalFunction);
                    toolsService.registerOverride("bindingTestTool", overrideFunction);
                    const toolCall = {
                        id: "test-call-3",
                        type: "function",
                        function: {
                            name: "bindingTestTool",
                            arguments: JSON.stringify({ data: "test" }),
                        },
                    };
                    const result = await toolsService.callTool(toolCall);
                    expect(result.functionResp).toBe("Override: test");
                });
                it("should maintain binding through wrappers", async () => {
                    const testTool = {
                        type: "function",
                        function: {
                            name: "wrapperBindingTest",
                            description: "Test tool for binding with wrappers",
                            parameters: {
                                type: "object",
                                properties: {
                                    value: { type: "string" },
                                },
                            },
                        },
                    };
                    function baseFunction(args) {
                        if (!this || typeof this.getContext !== "function") {
                            throw new Error("'this' is not bound to ToolsService instance in base function");
                        }
                        return `Base: ${args.value}`;
                    }
                    const wrapper = (originalFunc, args, tool) => {
                        const result = originalFunc.call(toolsService, ...args);
                        return `Wrapped: ${result}`;
                    };
                    toolsService.addTool(testTool);
                    toolsService.setFunction("wrapperBindingTest", baseFunction);
                    toolsService.registerWrapper("wrapperBindingTest", wrapper);
                    const toolCall = {
                        id: "test-call-4",
                        type: "function",
                        function: {
                            name: "wrapperBindingTest",
                            arguments: JSON.stringify({ value: "test" }),
                        },
                    };
                    const result = await toolsService.callTool(toolCall);
                    expect(result.functionResp).toBe("Wrapped: Base: test");
                });
            });
        });
    });
    describe("endsWith Tool Name Resolution", () => {
        it("should register tool with complex prefix", () => {
            const complexTool = {
                type: "function",
                function: {
                    name: "mcp_server_prefix_actualTool",
                    description: "A tool with a complex prefix",
                    parameters: {
                        type: "object",
                        properties: {
                            input: { type: "string", description: "Test input" },
                        },
                        required: ["input"],
                    },
                },
            };
            toolsService.addTool(complexTool);
            expect(toolsService.getTools()).toContain(complexTool);
            expect(toolsService.getToolNames()).toContain("mcp_server_prefix_actualTool");
        });
        it("should find tool using endsWith matching", () => {
            const complexTool = {
                type: "function",
                function: {
                    name: "mcp_server_prefix_actualTool",
                    description: "A tool with a complex prefix",
                    parameters: {
                        type: "object",
                        properties: {
                            input: { type: "string", description: "Test input" },
                        },
                        required: ["input"],
                    },
                },
            };
            toolsService.addTool(complexTool);
            const foundTool = toolsService.getTool("actualTool");
            expect(foundTool).toBeDefined();
            expect(foundTool.function.name).toBe("mcp_server_prefix_actualTool");
        });
        it("should call tool using partial name with endsWith", async () => {
            const complexTool = {
                type: "function",
                function: {
                    name: "mcp_server_prefix_testFunction",
                    description: "A tool with a complex prefix",
                    parameters: {
                        type: "object",
                        properties: {
                            message: { type: "string", description: "Test message" },
                        },
                        required: ["message"],
                    },
                },
            };
            const testFunction = (args) => {
                return `Received: ${args.message}`;
            };
            toolsService.addTool(complexTool);
            toolsService.setFunction("mcp_server_prefix_testFunction", testFunction);
            const toolCall = {
                id: "test-endsWith-1",
                type: "function",
                function: {
                    name: "testFunction",
                    arguments: JSON.stringify({ message: "Hello" }),
                },
            };
            const result = await toolsService.callTool(toolCall, [
                "mcp_server_prefix_testFunction",
            ]);
            expect(result.functionResp).toBe("Received: Hello");
            expect(result.functionName).toBe("testFunction");
        });
        it("should execute function correctly with endsWith resolution", async () => {
            const complexTool = {
                type: "function",
                function: {
                    name: "complex_prefix_myTool",
                    description: "Test tool with prefix",
                    parameters: {
                        type: "object",
                        properties: {
                            value: { type: "number", description: "A number" },
                        },
                        required: ["value"],
                    },
                },
            };
            const myFunction = (args) => {
                return args.value * 2;
            };
            toolsService.addTool(complexTool);
            toolsService.setFunction("complex_prefix_myTool", myFunction);
            const toolCall = {
                id: "test-endsWith-2",
                type: "function",
                function: {
                    name: "myTool",
                    arguments: JSON.stringify({ value: 5 }),
                },
            };
            const result = await toolsService.callTool(toolCall, [
                "complex_prefix_myTool",
            ]);
            expect(result.functionResp).toBe(10);
            expect(result.functionName).toBe("myTool");
        });
        it("should handle multiple tools with same suffix", () => {
            const tool1 = {
                type: "function",
                function: {
                    name: "prefix_a_sharedTool",
                    description: "First tool",
                    parameters: { type: "object", properties: {} },
                },
            };
            const tool2 = {
                type: "function",
                function: {
                    name: "prefix_b_sharedTool",
                    description: "Second tool",
                    parameters: { type: "object", properties: {} },
                },
            };
            toolsService.addTool(tool1);
            toolsService.addTool(tool2);
            const foundTool = toolsService.getTool("sharedTool");
            expect(foundTool).toBeDefined();
            expect(foundTool.function.name).toMatch(/_sharedTool$/);
        });
        it("should return first matching tool (no exact match priority)", () => {
            const prefixedTool = {
                type: "function",
                function: {
                    name: "prefix_myTool",
                    description: "Prefixed tool",
                    parameters: { type: "object", properties: {} },
                },
            };
            const exactMatchTool = {
                type: "function",
                function: {
                    name: "myTool",
                    description: "Exact match tool",
                    parameters: { type: "object", properties: {} },
                },
            };
            toolsService.addTool(prefixedTool);
            let foundTool = toolsService.getTool("myTool");
            expect(foundTool).toBeDefined();
            expect(foundTool.function.name).toBe("prefix_myTool");
            toolsService.addTool(exactMatchTool);
            foundTool = toolsService.getTool("myTool");
            expect(foundTool).toBeDefined();
            expect(foundTool.function.name).toBe("prefix_myTool");
        });
        it("should return undefined for non-matching partial name", () => {
            const tool = {
                type: "function",
                function: {
                    name: "mcp_server_myTool",
                    description: "A tool",
                    parameters: { type: "object", properties: {} },
                },
            };
            toolsService.addTool(tool);
            const foundTool = toolsService.getTool("nonExistent");
            expect(foundTool).toBeUndefined();
        });
        it("should work with getToolsByNames using endsWith logic for prefix stripping", () => {
            const tool1 = {
                type: "function",
                function: { name: "tool1", description: "", parameters: { type: "object", properties: {} } },
            };
            const tool2 = {
                type: "function",
                function: { name: "tool2", description: "", parameters: { type: "object", properties: {} } },
            };
            toolsService.addTools([tool1, tool2]);
            const foundTools = toolsService.getToolsByNames(["tool1", "tool2"]);
            expect(foundTools).toHaveLength(2);
            expect(foundTools.map(t => t.function.name)).toContain("tool1");
            expect(foundTools.map(t => t.function.name)).toContain("tool2");
        });
    });
});
//# sourceMappingURL=Tools.test.js.map