import type { AgentRunRequest } from "@rakazo/adapter-kit"; import { beforeEach, describe, expect, it, vi } from "vitest"; const state = vi.hoisted(() => ({ toolNames: [] as string[][] })); type FakeTool = { name: string; execute: (toolCallId: string, params: Record) => Promise; }; vi.mock("@earendil-works/pi-agent-core", () => ({ Agent: class { state = { errorMessage: undefined, messages: [] }; private readonly tools: FakeTool[]; constructor(options: { initialState: { tools: FakeTool[] } }) { this.tools = options.initialState.tools; state.toolNames.push(this.tools.map((tool) => tool.name)); } subscribe() {} async prompt() { await this.tools .find((tool) => tool.name === "run_subagent") ?.execute("subagent-call", { name: "helper", task: "help" }); } async waitForIdle() {} abort() {} }, })); vi.mock("@earendil-works/pi-ai/providers/all", () => ({ builtinModels: () => ({ getModel: () => ({ provider: "test", id: "fake-model" }), streamSimple: () => { throw new Error("No model requests are allowed"); }, }), })); vi.mock("./pi-local-provider.js", () => ({ registerLocalProvider: (models: unknown) => models, })); vi.mock("./pi-openai-compatible-provider.js", () => ({ OPENAI_COMPATIBLE_PROVIDER_ID: "openai-compatible", registerOpenAiCompatibleCatalog: (models: unknown) => models, registerOpenAiCompatibleRuntime: (models: unknown) => models, })); import { builtinAgentTools } from "./builtin-tools.js"; import { PiAgentRuntime } from "./pi-runtime.js"; async function run(tools: AgentRunRequest["tools"]) { for await (const _event of new PiAgentRuntime().run( { botId: "bot", threadId: "thread", runId: "run", prompt: "work", instructions: "test", history: [], tools, model: { provider: "test", id: "fake-model" }, executeTool: vi.fn(async () => ({ ok: true })), }, { signal: new AbortController().signal }, )) { // Exhaust the runtime event stream. } } describe("Pi runtime tool scope", () => { beforeEach(() => { state.toolNames = []; }); it("exposes no tools when the request lists none", async () => { await run([]); expect(state.toolNames).toEqual([[]]); }); it("gives a subagent only the parent's tools, minus delegation", async () => { await run( builtinAgentTools.filter((tool) => ["run_subagent", "read_file"].includes(tool.name)), ); expect(state.toolNames).toEqual([["read_file", "run_subagent"], ["read_file"]]); }); });