Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
113 lines
3.8 KiB
TypeScript
113 lines
3.8 KiB
TypeScript
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { getModel } from "@earendil-works/pi-ai/compat";
|
|
import type { TUI } from "@earendil-works/pi-tui";
|
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
import type { ToolDefinition } from "../src/core/extensions/types.ts";
|
|
import { DefaultResourceLoader } from "../src/core/resource-loader.ts";
|
|
import { createAgentSession } from "../src/core/sdk.ts";
|
|
import { SessionManager } from "../src/core/session-manager.ts";
|
|
import { SettingsManager } from "../src/core/settings-manager.ts";
|
|
import { ToolExecutionComponent } from "../src/modes/interactive/components/tool-execution.ts";
|
|
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
|
|
|
const examplesDir = join(import.meta.dirname, "../examples/extensions");
|
|
|
|
describe("tool renderer examples", () => {
|
|
let tempDir: string;
|
|
let agentDir: string;
|
|
|
|
beforeAll(() => {
|
|
initTheme("dark");
|
|
});
|
|
|
|
beforeEach(() => {
|
|
tempDir = join(tmpdir(), `pi-tool-renderer-example-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
agentDir = join(tempDir, "agent");
|
|
mkdirSync(agentDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (tempDir && existsSync(tempDir)) {
|
|
rmSync(tempDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
async function getSessionState(extensionPath: string | undefined, tools: string[]) {
|
|
const settingsManager = SettingsManager.inMemory();
|
|
const resourceLoader = new DefaultResourceLoader({
|
|
cwd: tempDir,
|
|
agentDir,
|
|
settingsManager,
|
|
noExtensions: true,
|
|
noSkills: true,
|
|
noPromptTemplates: true,
|
|
noThemes: true,
|
|
noContextFiles: true,
|
|
additionalExtensionPaths: extensionPath ? [extensionPath] : [],
|
|
});
|
|
await resourceLoader.reload();
|
|
expect(resourceLoader.getExtensions().errors).toEqual([]);
|
|
const { session } = await createAgentSession({
|
|
cwd: tempDir,
|
|
agentDir,
|
|
model: getModel("anthropic", "claude-sonnet-4-5"),
|
|
settingsManager,
|
|
sessionManager: SessionManager.inMemory(tempDir),
|
|
resourceLoader,
|
|
tools,
|
|
});
|
|
try {
|
|
return {
|
|
systemPrompt: session.systemPrompt,
|
|
editToolDefinition: session.getToolDefinition("edit"),
|
|
};
|
|
} finally {
|
|
session.dispose();
|
|
}
|
|
}
|
|
|
|
it.each([
|
|
{
|
|
name: "built-in tool renderer",
|
|
extensionPath: join(examplesDir, "built-in-tool-renderer.ts"),
|
|
tools: ["read", "bash", "edit", "write"],
|
|
},
|
|
{
|
|
name: "minimal mode",
|
|
extensionPath: join(examplesDir, "minimal-mode.ts"),
|
|
tools: ["read", "bash", "write", "edit", "find", "grep", "ls"],
|
|
},
|
|
])("keeps the system prompt unchanged for the $name example", async ({ extensionPath, tools }) => {
|
|
// Regression test for https://github.com/earendil-works/pi/issues/10072
|
|
const baseline = await getSessionState(undefined, tools);
|
|
const withRenderer = await getSessionState(extensionPath, tools);
|
|
|
|
expect(withRenderer.systemPrompt).toBe(baseline.systemPrompt);
|
|
});
|
|
|
|
it("keeps minimal mode's edit tool in the default shell", async () => {
|
|
// Regression test for https://github.com/earendil-works/pi/issues/10072
|
|
const minimalMode = await getSessionState(join(examplesDir, "minimal-mode.ts"), ["edit"]);
|
|
const editToolDefinition = minimalMode.editToolDefinition;
|
|
if (!editToolDefinition) throw new Error("minimal mode did not register the edit tool");
|
|
const { renderShell: _renderShell, ...defaultShellDefinition } = editToolDefinition;
|
|
|
|
expect(renderEditTool(editToolDefinition, tempDir)).toEqual(renderEditTool(defaultShellDefinition, tempDir));
|
|
});
|
|
});
|
|
|
|
function renderEditTool(definition: ToolDefinition, cwd: string): string[] {
|
|
const component = new ToolExecutionComponent(
|
|
"edit",
|
|
"edit-shell-test",
|
|
{ path: "notes.txt", oldText: "before", newText: "after" },
|
|
{},
|
|
definition,
|
|
{ requestRender() {} } as unknown as TUI,
|
|
cwd,
|
|
);
|
|
return component.render(40);
|
|
}
|