import { describe, expect, it } from "vitest";
import { z } from "zod";
import * as z3 from "zod/v3";
import * as z4 from "zod/v4";
import { voice } from "../index.js";
import {
  CONFIRM_DUPLICATE_PARAM,
  ProviderTool,
  ToolContext,
  ToolFlag,
  Toolset,
  tool
} from "./tool_context.js";
import { createToolOptions, oaiParams } from "./utils.js";
describe("tools object compatibility", () => {
  it("normalizes object tools into named function tools", () => {
    const getWeather = tool({
      description: "Get the weather",
      execute: async () => "sunny"
    });
    const toolCtx = new ToolContext({
      getWeather
    });
    const namedTool = toolCtx.getFunctionTool("getWeather");
    expect(namedTool).toBeDefined();
    expect(namedTool == null ? void 0 : namedTool.name).toBe("getWeather");
    expect(namedTool == null ? void 0 : namedTool.id).toBe("getWeather");
    expect(namedTool == null ? void 0 : namedTool.description).toBe("Get the weather");
    expect(toolCtx.tools).toEqual([namedTool]);
  });
  it("accepts object tools in agent and session construction APIs", () => {
    var _a, _b, _c, _d, _e;
    const tools = {
      lookupOrder: tool({
        description: "Look up an order",
        execute: async () => ({ ok: true })
      })
    };
    const agent = new voice.Agent({ instructions: "help", tools });
    const createdAgent = voice.Agent.create({ instructions: "help", tools });
    const task = new voice.AgentTask({ instructions: "help", tools });
    const createdTask = voice.AgentTask.create({ instructions: "help", tools });
    const session = new voice.AgentSession({ tools, vad: null });
    expect((_a = agent.toolCtx.getFunctionTool("lookupOrder")) == null ? void 0 : _a.name).toBe("lookupOrder");
    expect((_b = createdAgent.toolCtx.getFunctionTool("lookupOrder")) == null ? void 0 : _b.name).toBe("lookupOrder");
    expect((_c = task.toolCtx.getFunctionTool("lookupOrder")) == null ? void 0 : _c.name).toBe("lookupOrder");
    expect((_d = createdTask.toolCtx.getFunctionTool("lookupOrder")) == null ? void 0 : _d.name).toBe("lookupOrder");
    expect((_e = session.tools[0]) == null ? void 0 : _e.name).toBe("lookupOrder");
  });
  it("keeps rejecting duplicate named function tools in array syntax", () => {
    const first = tool({
      name: "duplicate",
      description: "First tool",
      execute: async () => "first"
    });
    const second = tool({
      name: "duplicate",
      description: "Second tool",
      execute: async () => "second"
    });
    expect(() => new ToolContext([first, second])).toThrow("duplicate function name");
  });
  it("rejects named tools and toolsets in object syntax", () => {
    const namedTool = tool({
      name: "alreadyNamed",
      description: "Already named",
      execute: async () => "ok"
    });
    const toolset = new Toolset({ id: "group", tools: [] });
    expect(() => new ToolContext({ alreadyNamed: namedTool })).toThrow(
      'tools object entry "alreadyNamed" must be anonymous'
    );
    expect(() => new ToolContext({ group: toolset })).toThrow(
      'tools object entry "group" must be an anonymous function tool'
    );
  });
});
describe("Tool Context", () => {
  describe("oaiParams", () => {
    it("should handle basic object schema", () => {
      const schema = z.object({
        name: z.string().describe("The user name"),
        age: z.number().describe("The user age")
      });
      const result = oaiParams(schema);
      expect(result).toMatchSnapshot();
    });
    it("should handle enum fields", () => {
      const schema = z.object({
        color: z.enum(["red", "blue", "green"]).describe("Choose a color")
      });
      const result = oaiParams(schema);
      expect(result).toMatchSnapshot();
    });
    it("should handle array fields", () => {
      const schema = z.object({
        tags: z.array(z.string()).describe("List of tags")
      });
      const result = oaiParams(schema);
      expect(result).toMatchSnapshot();
    });
    it("should handle array of enums", () => {
      const schema = z.object({
        colors: z.array(z.enum(["red", "blue", "green"])).describe("List of colors")
      });
      const result = oaiParams(schema);
      expect(result).toMatchSnapshot();
    });
    it("should handle optional fields", () => {
      const schema = z.object({
        name: z.string().describe("The user name"),
        age: z.number().optional().describe("The user age")
      });
      const result = oaiParams(schema);
      expect(result).toMatchSnapshot();
    });
    it("should handle fields without descriptions", () => {
      const schema = z.object({
        name: z.string(),
        age: z.number()
      });
      const result = oaiParams(schema);
      expect(result).toMatchSnapshot();
    });
  });
  describe("tool", () => {
    it("should create and execute a basic core tool", async () => {
      const getWeather = tool({
        name: "getWeather",
        description: "Get the weather for a given location",
        parameters: z.object({
          location: z.string()
        }),
        execute: async ({ location }, { ctx }) => {
          return `The weather in ${location} is sunny, ${ctx.userData.name}`;
        }
      });
      const result = await getWeather.execute(
        { location: "San Francisco" },
        createToolOptions("123", { name: "John" })
      );
      expect(result).toBe("The weather in San Francisco is sunny, John");
    });
    it("defaults to allow duplicate calls and no flags", () => {
      const testFunction = tool({
        name: "defaultDuplicatePolicy",
        description: "Test default duplicate policy",
        execute: async () => "ok"
      });
      expect(testFunction.onDuplicate).toBe("allow");
      expect(testFunction.flags).toBe(ToolFlag.NONE);
    });
    it("stores cancellable flag and duplicate policy", () => {
      const testFunction = tool({
        name: "cancellableDuplicatePolicy",
        description: "Test duplicate policy",
        execute: async () => "ok",
        flags: ToolFlag.CANCELLABLE,
        onDuplicate: "replace"
      });
      expect(testFunction.onDuplicate).toBe("replace");
      expect(testFunction.flags & ToolFlag.CANCELLABLE).toBeTruthy();
    });
    it("injects and strips confirm duplicate parameter for zod schemas", async () => {
      let receivedArgs;
      const testFunction = tool({
        name: "confirmDuplicateZod",
        description: "Test confirm duplicate with zod",
        parameters: z.object({
          query: z.string()
        }),
        onDuplicate: "confirm",
        execute: async (args) => {
          receivedArgs = args;
          return "ok";
        }
      });
      const params = oaiParams(testFunction.parameters);
      expect(params.properties).toHaveProperty(CONFIRM_DUPLICATE_PARAM);
      expect(params.required).toContain(CONFIRM_DUPLICATE_PARAM);
      const result = await testFunction.execute(
        { query: "flights", [CONFIRM_DUPLICATE_PARAM]: true },
        createToolOptions("confirm-zod")
      );
      expect(result).toBe("ok");
      expect(receivedArgs).toEqual({ query: "flights" });
    });
    it("injects and strips confirm duplicate parameter for raw JSON schemas", async () => {
      let receivedArgs;
      const testFunction = tool({
        name: "confirmDuplicateRaw",
        description: "Test confirm duplicate with raw schema",
        parameters: {
          type: "object",
          properties: {
            query: { type: "string" }
          },
          required: ["query"]
        },
        onDuplicate: "confirm",
        execute: async (args) => {
          receivedArgs = args;
          return "ok";
        }
      });
      expect(testFunction.parameters).toMatchObject({
        properties: {
          [CONFIRM_DUPLICATE_PARAM]: {
            type: ["boolean", "null"]
          }
        }
      });
      expect(testFunction.parameters.required).toContain(
        CONFIRM_DUPLICATE_PARAM
      );
      const result = await testFunction.execute(
        { query: "flights", [CONFIRM_DUPLICATE_PARAM]: true },
        createToolOptions("confirm-raw")
      );
      expect(result).toBe("ok");
      expect(receivedArgs).toEqual({ query: "flights" });
    });
    it("should properly type a callable function", async () => {
      const testFunction = tool({
        name: "testFunction",
        description: "Test function",
        parameters: z.object({
          name: z.string().describe("The user name"),
          age: z.number().describe("The user age")
        }),
        execute: async (args) => {
          return `${args.name} is ${args.age} years old`;
        }
      });
      const result = await testFunction.execute(
        { name: "John", age: 30 },
        createToolOptions("123")
      );
      expect(result).toBe("John is 30 years old");
    });
    it("should handle async execution", async () => {
      const testFunction = tool({
        name: "asyncTestFunction",
        description: "Async test function",
        parameters: z.object({
          delay: z.number().describe("Delay in milliseconds")
        }),
        execute: async (args) => {
          await new Promise((resolve) => setTimeout(resolve, args.delay));
          return args.delay;
        }
      });
      const start = Date.now();
      const result = await testFunction.execute({ delay: 100 }, createToolOptions("123"));
      const duration = Date.now() - start;
      expect(result).toBe(100);
      expect(duration).toBeGreaterThanOrEqual(95);
    });
    describe("nested array support", () => {
      it("should handle nested array fields", () => {
        const schema = z.object({
          items: z.array(
            z.object({
              name: z.string().describe("the item name"),
              modifiers: z.array(
                z.object({
                  modifier_name: z.string(),
                  modifier_value: z.string()
                })
              ).describe("list of the modifiers applied on this item, such as size")
            })
          )
        });
        const result = oaiParams(schema);
        expect(result).toMatchSnapshot();
      });
    });
    describe("optional parameters", () => {
      it("should create a tool without parameters", async () => {
        const simpleAction = tool({
          name: "simpleAction",
          description: "Perform a simple action",
          execute: async () => {
            return "Action performed";
          }
        });
        expect(simpleAction.type).toBe("function");
        expect(simpleAction.description).toBe("Perform a simple action");
        expect(simpleAction.parameters).toBeDefined();
        expect(simpleAction.parameters._def.typeName).toBe("ZodObject");
        const result = await simpleAction.execute({}, createToolOptions("123"));
        expect(result).toBe("Action performed");
      });
      it("should support .optional() fields in tool parameters", async () => {
        const weatherTool = tool({
          name: "weatherTool",
          description: "Get weather information",
          parameters: z.object({
            location: z.string().describe("The city or location").optional(),
            units: z.enum(["celsius", "fahrenheit"]).describe("Temperature units").optional()
          }),
          execute: async ({ location, units }) => {
            const loc = location ?? "Unknown";
            const unit = units ?? "celsius";
            return `Weather in ${loc} (${unit})`;
          }
        });
        expect(weatherTool.type).toBe("function");
        expect(weatherTool.description).toBe("Get weather information");
        const result1 = await weatherTool.execute(
          { location: "London", units: "celsius" },
          createToolOptions("123")
        );
        expect(result1).toBe("Weather in London (celsius)");
        const result2 = await weatherTool.execute({}, createToolOptions("123"));
        expect(result2).toBe("Weather in Unknown (celsius)");
        const result3 = await weatherTool.execute({ location: "Paris" }, createToolOptions("123"));
        expect(result3).toBe("Weather in Paris (celsius)");
      });
      it("should handle tools with context but no parameters", async () => {
        const greetUser = tool({
          name: "greetUser",
          description: "Greet the current user",
          execute: async (_, { ctx }) => {
            return `Hello, ${ctx.userData.username}!`;
          }
        });
        const result = await greetUser.execute({}, createToolOptions("123", { username: "Alice" }));
        expect(result).toBe("Hello, Alice!");
      });
      it("should create a tool that accesses tool call id without parameters", async () => {
        const getCallId = tool({
          name: "getCallId",
          description: "Get the current tool call ID",
          execute: async (_, { toolCallId }) => {
            return `Tool call ID: ${toolCallId}`;
          }
        });
        const result = await getCallId.execute({}, createToolOptions("test-id-456"));
        expect(result).toBe("Tool call ID: test-id-456");
      });
    });
    describe("Zod v3 and v4 compatibility", () => {
      it("should work with Zod v3 schemas", async () => {
        const v3Tool = tool({
          name: "v3Tool",
          description: "A tool using Zod v3 schema",
          parameters: z3.object({
            name: z3.string(),
            count: z3.number()
          }),
          execute: async ({ name, count }) => {
            return `${name}: ${count}`;
          }
        });
        const result = await v3Tool.execute(
          { name: "Test", count: 42 },
          createToolOptions("v3-test")
        );
        expect(result).toBe("Test: 42");
      });
      it("should work with Zod v4 schemas", async () => {
        const v4Tool = tool({
          name: "v4Tool",
          description: "A tool using Zod v4 schema",
          parameters: z4.object({
            name: z4.string(),
            count: z4.number()
          }),
          execute: async ({ name, count }) => {
            return `${name}: ${count}`;
          }
        });
        const result = await v4Tool.execute(
          { name: "Test", count: 42 },
          createToolOptions("v4-test")
        );
        expect(result).toBe("Test: 42");
      });
      it("should handle v4 schemas with optional fields", async () => {
        const v4Tool = tool({
          name: "v4OptionalTool",
          description: "Tool with optional field using v4",
          parameters: z4.object({
            required: z4.string(),
            optional: z4.string().optional()
          }),
          execute: async ({ required, optional }) => {
            return optional ? `${required} - ${optional}` : required;
          }
        });
        const result1 = await v4Tool.execute({ required: "Hello" }, createToolOptions("test-1"));
        expect(result1).toBe("Hello");
        const result2 = await v4Tool.execute(
          { required: "Hello", optional: "World" },
          createToolOptions("test-2")
        );
        expect(result2).toBe("Hello - World");
      });
      it("should handle v4 enum schemas", async () => {
        const v4Tool = tool({
          name: "v4EnumTool",
          description: "Tool with enum using v4",
          parameters: z4.object({
            color: z4.enum(["red", "blue", "green"])
          }),
          execute: async ({ color }) => {
            return `Selected color: ${color}`;
          }
        });
        const result = await v4Tool.execute({ color: "blue" }, createToolOptions("test-enum"));
        expect(result).toBe("Selected color: blue");
      });
      it("should handle v4 array schemas", async () => {
        const v4Tool = tool({
          name: "v4ArrayTool",
          description: "Tool with array using v4",
          parameters: z4.object({
            tags: z4.array(z4.string())
          }),
          execute: async ({ tags }) => {
            return `Tags: ${tags.join(", ")}`;
          }
        });
        const result = await v4Tool.execute(
          { tags: ["nodejs", "typescript", "testing"] },
          createToolOptions("test-array")
        );
        expect(result).toBe("Tags: nodejs, typescript, testing");
      });
      it("should handle v4 nested object schemas", async () => {
        const v4Tool = tool({
          name: "v4NestedTool",
          description: "Tool with nested object using v4",
          parameters: z4.object({
            user: z4.object({
              name: z4.string(),
              email: z4.string()
            })
          }),
          execute: async ({ user }) => {
            return `${user.name} (${user.email})`;
          }
        });
        const result = await v4Tool.execute(
          { user: { name: "John Doe", email: "john@example.com" } },
          createToolOptions("test-nested")
        );
        expect(result).toBe("John Doe (john@example.com)");
      });
    });
    describe("oaiParams with v4 schemas", () => {
      it("should convert v4 basic object schema", () => {
        const schema = z4.object({
          name: z4.string().describe("User name"),
          age: z4.number().describe("User age")
        });
        const result = oaiParams(schema);
        expect(result.type).toBe("object");
        expect(result.properties).toHaveProperty("name");
        expect(result.properties).toHaveProperty("age");
        expect(result.required).toContain("name");
        expect(result.required).toContain("age");
      });
      it("should handle v4 optional fields", () => {
        const schema = z4.object({
          required: z4.string(),
          optional: z4.string().optional()
        });
        const result = oaiParams(schema);
        expect(result.required).toContain("required");
        expect(result.required).not.toContain("optional");
      });
      it("should handle v4 enum fields", () => {
        var _a;
        const schema = z4.object({
          status: z4.enum(["pending", "approved", "rejected"])
        });
        const result = oaiParams(schema);
        const properties = result.properties;
        expect((_a = properties.status) == null ? void 0 : _a.enum).toEqual(["pending", "approved", "rejected"]);
      });
      it("should handle v4 array fields", () => {
        const schema = z4.object({
          items: z4.array(z4.string())
        });
        const result = oaiParams(schema);
        const properties = result.properties;
        expect(
          properties.items && typeof properties.items === "object" ? properties.items.type : void 0
        ).toBe("array");
        expect(
          properties.items && properties.items.items && typeof properties.items.items === "object" ? properties.items.items.type : void 0
        ).toBe("string");
      });
    });
  });
});
describe("tool() name requirement", () => {
  it("creates an anonymous function tool when name is missing", () => {
    const t = tool({
      description: "no name",
      execute: async () => "x"
    });
    expect(t.description).toBe("no name");
    expect("name" in t).toBe(false);
    expect("id" in t).toBe(false);
  });
  it("throws when name is empty", () => {
    expect(
      () => tool({
        name: "",
        description: "empty name",
        execute: async () => "x"
      })
    ).toThrow("requires a non-empty name");
  });
  it("stores the name on the returned function tool", () => {
    const t = tool({
      name: "doStuff",
      description: "d",
      execute: async () => "x"
    });
    expect(t.name).toBe("doStuff");
  });
  it("exposes id mirroring the function tool name", () => {
    const t = tool({
      name: "doStuff",
      description: "d",
      execute: async () => "x"
    });
    expect(t.id).toBe("doStuff");
    expect(t.id).toBe(t.name);
  });
});
class TestProviderTool extends ProviderTool {
}
describe("ToolContext", () => {
  const makeFn = (name) => tool({
    name,
    description: `${name} tool`,
    execute: async () => name
  });
  it("empty() returns an empty context", () => {
    const ctx = ToolContext.empty();
    expect(ctx.functionTools).toEqual({});
    expect(ctx.providerTools).toEqual([]);
    expect(ctx.toolsets).toEqual([]);
    expect(ctx.flatten()).toEqual([]);
  });
  it("indexes function tools by name and supports lookup", () => {
    const a = makeFn("a");
    const b = makeFn("b");
    const ctx = new ToolContext([a, b]);
    expect(ctx.functionTools).toEqual({ a, b });
    expect(ctx.getFunctionTool("a")).toBe(a);
    expect(ctx.getFunctionTool("b")).toBe(b);
    expect(ctx.getFunctionTool("missing")).toBeUndefined();
  });
  it("throws on duplicate function names with different instances", () => {
    const a1 = makeFn("a");
    const a2 = makeFn("a");
    expect(() => new ToolContext([a1, a2])).toThrow("duplicate function name: a");
  });
  it("silently skips the same function tool instance listed multiple times", () => {
    const a = makeFn("a");
    const ctx = new ToolContext([a, a]);
    expect(ctx.getFunctionTool("a")).toBe(a);
    expect(Object.keys(ctx.functionTools)).toEqual(["a"]);
  });
  it("separates provider tools from function tools", () => {
    const fnA = makeFn("a");
    const provider = new TestProviderTool({ id: "code" });
    const ctx = new ToolContext([fnA, provider]);
    expect(ctx.functionTools).toEqual({ a: fnA });
    expect(ctx.providerTools).toEqual([provider]);
    expect(ctx.flatten()).toEqual([fnA, provider]);
  });
  it("updateTools replaces the entire context", () => {
    const a = makeFn("a");
    const b = makeFn("b");
    const ctx = new ToolContext([a]);
    ctx.updateTools([b]);
    expect(ctx.getFunctionTool("a")).toBeUndefined();
    expect(ctx.getFunctionTool("b")).toBe(b);
  });
  it("copy() yields an independent context with the same tools", () => {
    const a = makeFn("a");
    const ctx = new ToolContext([a]);
    const dup = ctx.copy();
    expect(dup.getFunctionTool("a")).toBe(a);
    dup.updateTools([]);
    expect(ctx.getFunctionTool("a")).toBe(a);
    expect(dup.getFunctionTool("a")).toBeUndefined();
  });
  it("equals() compares function tool maps and provider lists by identity", () => {
    const a = makeFn("a");
    const b = makeFn("b");
    const c = makeFn("c");
    expect(new ToolContext([a, b]).equals(new ToolContext([a, b]))).toBe(true);
    expect(new ToolContext([a, b]).equals(new ToolContext([a]))).toBe(false);
    expect(new ToolContext([a, b]).equals(new ToolContext([a, c]))).toBe(false);
  });
  it("equals() is reflexive", () => {
    const a = makeFn("a");
    const provider = new TestProviderTool({ id: "code" });
    const ctx = new ToolContext([a, provider]);
    expect(ctx.equals(ctx)).toBe(true);
  });
  it("equals() treats provider tool order as insignificant", () => {
    const a = makeFn("a");
    const p1 = new TestProviderTool({ id: "code" });
    const p2 = new TestProviderTool({ id: "browser" });
    expect(new ToolContext([a, p1, p2]).equals(new ToolContext([a, p2, p1]))).toBe(true);
  });
  it("equals() supports contexts with only provider tools", () => {
    const p1 = new TestProviderTool({ id: "code" });
    const p2 = new TestProviderTool({ id: "browser" });
    expect(new ToolContext([p1, p2]).equals(new ToolContext([p1, p2]))).toBe(true);
    const p3 = new TestProviderTool({ id: "code" });
    expect(new ToolContext([p1]).equals(new ToolContext([p3]))).toBe(false);
  });
  it("hasTool() matches function tools by name and provider tools by id", () => {
    const a = makeFn("a");
    const provider = new TestProviderTool({ id: "code_runner" });
    const ctx = new ToolContext([a, provider]);
    expect(ctx.hasTool("a")).toBe(true);
    expect(ctx.hasTool("code_runner")).toBe(true);
    expect(ctx.hasTool("missing")).toBe(false);
  });
  it("flatten() returns function tools in insertion order followed by provider tools", () => {
    const a = makeFn("a");
    const b = makeFn("b");
    const provider = new TestProviderTool({ id: "code" });
    const ctx = new ToolContext([b, provider, a]);
    expect(ctx.flatten()).toEqual([b, a, provider]);
  });
});
describe("Toolset", () => {
  const makeFn = (name) => tool({
    name,
    description: `${name} tool`,
    execute: async () => name
  });
  it("exposes its id and the tools it was constructed with", () => {
    const a = makeFn("a");
    const b = makeFn("b");
    const ts = new Toolset({ id: "set1", tools: [a, b] });
    expect(ts.id).toBe("set1");
    expect(ts.tools).toEqual([a, b]);
  });
  const fakeToolsetContext = (updateTools = () => {
  }) => ({ updateTools });
  it("default setup and aclose are no-ops", async () => {
    const ts = new Toolset({ id: "noop", tools: [] });
    await expect(ts.setup(fakeToolsetContext())).resolves.toBeUndefined();
    await expect(ts.aclose()).resolves.toBeUndefined();
  });
  it("lets subclasses override lifecycle hooks", async () => {
    const events = [];
    class Recording extends Toolset {
      async setup(_ctx) {
        events.push(`setup:${this.id}`);
      }
      async aclose() {
        events.push(`close:${this.id}`);
      }
    }
    const ts = new Recording({ id: "rec", tools: [] });
    await ts.setup(fakeToolsetContext());
    await ts.aclose();
    expect(events).toEqual(["setup:rec", "close:rec"]);
  });
  it("Toolset.create() resolves a static tools list eagerly and composes aclose", async () => {
    const a = makeFn("a");
    const events = [];
    const ts = Toolset.create({
      id: "composed",
      tools: [a],
      aclose: async () => {
        events.push("close");
      }
    });
    expect(ts).toBeInstanceOf(Toolset);
    expect(ts.id).toBe("composed");
    expect(ts.tools).toEqual([a]);
    await ts.setup(fakeToolsetContext());
    await ts.aclose();
    expect(events).toEqual(["close"]);
  });
  it("Toolset.create() defaults aclose to a no-op when omitted", async () => {
    const ts = Toolset.create({ id: "bare", tools: [] });
    await expect(ts.setup(fakeToolsetContext())).resolves.toBeUndefined();
    await expect(ts.aclose()).resolves.toBeUndefined();
  });
  it("lets setup push tools after activation via ctx.updateTools", async () => {
    const a = makeFn("a");
    const b = makeFn("b");
    let push;
    const ts = Toolset.create({
      id: "mcp",
      setup: async ({ updateTools }) => {
        push = updateTools;
      },
      tools: []
    });
    await ts.setup(fakeToolsetContext((tools) => ts._setTools(tools)));
    expect(ts.tools).toEqual([]);
    push([a, b]);
    expect(ts.tools).toEqual([a, b]);
  });
  it("is flattened into a ToolContext: function tools merged, toolset tracked", () => {
    const a = makeFn("a");
    const b = makeFn("b");
    const ts = new Toolset({ id: "set", tools: [a, b] });
    const direct = makeFn("direct");
    const ctx = new ToolContext([direct, ts]);
    expect(Object.keys(ctx.functionTools).sort()).toEqual(["a", "b", "direct"]);
    expect(ctx.toolsets).toEqual([ts]);
  });
  it("throws when a Toolset contributes a duplicate function name", () => {
    const a1 = makeFn("a");
    const a2 = makeFn("a");
    const ts = new Toolset({ id: "collides", tools: [a2] });
    expect(() => new ToolContext([a1, ts])).toThrow(/duplicate function name: a/);
  });
  it("equals() compares toolsets as identity sets, not by order", () => {
    const ts1 = new Toolset({ id: "one", tools: [] });
    const ts2 = new Toolset({ id: "two", tools: [] });
    expect(new ToolContext([ts1, ts2]).equals(new ToolContext([ts2, ts1]))).toBe(true);
    const ts3 = new Toolset({ id: "three", tools: [] });
    expect(new ToolContext([ts1, ts2]).equals(new ToolContext([ts1, ts3]))).toBe(false);
    expect(new ToolContext([ts1]).equals(new ToolContext([ts1, ts2]))).toBe(false);
  });
});
//# sourceMappingURL=tool_context.test.js.map