Z ZPI 文档 v0.3.8
介绍页

源码

packages/coding-agent/test/tool-overrides.test.ts

typescript109 行查看原文
import {
  createAgentSession,
  createBashToolDefinition,
  createEditToolDefinition,
  createReadToolDefinition,
  createWriteToolDefinition,
  ModelRuntime,
  SessionManager,
  type ToolDefinition,
} from "@zpi-dev/coding-agent";
import { Type } from "typebox";
import { expect, test } from "vitest";
import { fakeConfig } from "../../../tests/fake-server.ts";
import { directory } from "./helpers/session-fixture.ts";

test("explicit built-in overrides receive session context without removing other tools or permitting duplicate names", async () => {
  const cwd = await directory();
  const runtime = await ModelRuntime.create();
  runtime.registerProvider("fake", {
    baseUrl: "http://127.0.0.1:1",
    models: [fakeConfig("")],
    apiKey: "local",
  });
  const override: ToolDefinition = {
    name: "bash",
    label: "Custom shell",
    description: "Custom shell",
    parameters: Type.Object({ command: Type.String() }),
    execute: async (_id, _params, _signal, _update, context) => ({
      content: [{ type: "text", text: context.cwd }],
      details: undefined,
    }),
  };
  const options = {
    cwd,
    agentDir: cwd,
    homeDir: cwd,
    modelRuntime: runtime,
    sessionManager: SessionManager.inMemory(cwd),
  };
  const { session } = await createAgentSession({ ...options, toolOverrides: [override] });
  try {
    expect(session.state.tools.map((tool) => tool.name)).toEqual(["read", "bash", "edit", "write"]);
    const bash = session.state.tools.find((tool) => tool.name === "bash");
    if (!bash) throw new Error("Missing shell");
    expect(await bash.execute("call", { command: "test" })).toMatchObject({ content: [{ text: cwd }] });
    await expect(
      createAgentSession({ ...options, toolOverrides: [{ ...override, name: "missing" }] }),
    ).rejects.toThrow("Unknown built-in");
    await expect(createAgentSession({ ...options, toolOverrides: [override, override] })).rejects.toThrow(
      "Duplicate tool overrides",
    );
    await expect(createAgentSession({ ...options, customTools: [override] })).rejects.toThrow(
      "Duplicate tool names",
    );
  } finally {
    session.dispose();
  }
});

test("public built-in factories register as overrides and preserve standalone execution", async () => {
  const cwd = await directory();
  const runtime = await ModelRuntime.create();
  runtime.registerProvider("fake", {
    baseUrl: "http://127.0.0.1:1",
    models: [fakeConfig("")],
    apiKey: "local",
  });
  const definitions = [
    createReadToolDefinition(cwd),
    createBashToolDefinition(cwd),
    createEditToolDefinition(cwd),
    createWriteToolDefinition(cwd),
  ];
  const manager = SessionManager.inMemory(cwd);
  const { session } = await createAgentSession({
    cwd,
    agentDir: cwd,
    homeDir: cwd,
    modelRuntime: runtime,
    sessionManager: manager,
    toolOverrides: definitions,
  });
  try {
    expect(session.state.tools.map((tool) => tool.name)).toEqual(["read", "bash", "edit", "write"]);
    const tool = (name: string) => {
      const tool = session.state.tools.find((tool) => tool.name === name);
      if (!tool) throw new Error(`Missing tool: ${name}`);
      return tool;
    };
    await tool("write").execute("write", { path: "example.txt", content: "before" });
    await tool("edit").execute("edit", {
      path: "example.txt",
      edits: [{ oldText: "before", newText: "after" }],
    });
    expect((await tool("read").execute("read", { path: "example.txt" })).content).toEqual([
      { type: "text", text: "after" },
    ]);
    const bash = await tool("bash").execute("bash", { command: 'printf "%s" "$ZPI_SESSION_ID"' });
    expect(bash.content).toEqual([{ type: "text", text: manager.getSessionId() }]);
    // Factories are also usable directly without a session or execution context.
    expect(
      (await createReadToolDefinition(cwd).execute("standalone", { path: "example.txt" })).content,
    ).toEqual([{ type: "text", text: "after" }]);
  } finally {
    session.dispose();
  }
});