import { assert, assertEquals, assertRejects, assertStringIncludes } from "@std/assert"; import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context"; import { Type } from "@earendil-works/pi-ai"; import { fauxAssistantMessage, fauxToolCall } from "@earendil-works/pi-ai/providers/faux"; import { defineExtension } from "@berry/api"; import { localEnvironment } from "@berry/api/environment.ts"; import { onPath, tempDir } from "@berry/util"; import code from "../extensions/code/mod.ts"; import { ShellEntry } from "./shell.ts"; import { lastToolResult, script, sectionsSeen, setup, toolsOffered, turn } from "./fixture.ts"; Deno.test("environment-gated tools disappear from both catalogs and cannot be called while disabled", async () => { await using root = await tempDir(); let offline = false; await using h = await setup({ open: (name, cwd) => { if (offline) return Promise.reject(new Error("offline")); return Promise.resolve(Object.assign(localEnvironment(cwd ?? root.path), { name, platform: name === "windows" ? "win32" : "linux", })); }, close: () => Promise.resolve(), }); let calls = 0; await h.berry.install(defineExtension({ name: "conditional", initial: () => null, setup(ext) { ext.tool({ name: "windowsOnly", description: "Windows only", parameters: Type.Object({}), expose: "both", enabled: ({ env }) => Promise.resolve(env?.platform === "win32"), execute: () => ++calls, }); }, })); await h.berry.install(code); const tid = await h.berry.create({ extensions: { conditional: {}, code: {} }, model: h.model }); const direct = fauxAssistantMessage(fauxToolCall("windowsOnly", {}), { stopReason: "toolUse" }); const disabled = await turn(h, tid, [direct, script("return await tools.windowsOnly({})")]); assert(!toolsOffered(disabled[0]).includes("windowsOnly")); assert(!toolsOffered(disabled[0]).includes("powershell")); const catalog = sectionsSeen(disabled[0]).at(-1)?.codemode ?? ""; assert(!catalog.includes("windowsOnly")); assert(!catalog.includes("powershell")); assert(lastToolResult(disabled[1]).isError); assert(lastToolResult(disabled[2]).isError); assertEquals(calls, 0); await h.berry.configureExtension(tid, "environment", { environment: "windows" }); const enabled = await turn(h, tid, [direct, script("return await tools.windowsOnly({})")]); assert(toolsOffered(enabled[0]).includes("powershell")); assertStringIncludes(sectionsSeen(enabled[0]).at(-1)?.codemode ?? "", "powershell:"); assertEquals(calls, 2); offline = true; const unavailable = await turn(h, tid, [ script("return await tools.powershell({ command: 'Get-Location', timeout: 10 })"), ]); assert(!sectionsSeen(unavailable[0]).at(-1)?.codemode?.includes("powershell")); assert(lastToolResult(unavailable[1]).isError); offline = false; await h.berry.configureExtension(tid, "environment", { environment: "local" }); const [again] = await turn(h, tid, []); assert(!toolsOffered(again).includes("powershell")); assert(!toolsOffered(again).includes("windowsOnly")); assert(!sectionsSeen(again).at(-1)?.codemode?.includes("powershell")); }); Deno.test({ name: "workspace command follows the target jj workspace and disappears for unavailable targets", ignore: !onPath("jj"), async fn() { await using root = await tempDir(); const repo = root.path + "/repo"; const nested = repo + "/nested"; const secondary = root.path + "/secondary"; for ( const args of [ ["git", "init", repo], ["-R", repo, "workspace", "add", secondary], ] ) { const result = await new Deno.Command("jj", { args, stdout: "null", stderr: "piped" }) .output(); assert(result.success, new TextDecoder().decode(result.stderr)); } await Deno.mkdir(nested); let offline = false; await using h = await setup({ open: (name, cwd) => { if (offline) return Promise.reject(new Error("offline")); const env = localEnvironment(cwd ?? root.path); if (name === "no-jj") env.exec = () => Promise.reject(new Error("jj missing")); return Promise.resolve(env); }, close: () => Promise.resolve(), }); await h.berry.install(code); const tid = await h.berry.create({ extensions: { code: {} }, model: h.model }); const offered = async () => (await h.berry.commands(tid)).some(({ name }) => name === "workspace"); assertEquals(await offered(), false); await assertRejects( () => h.berry.command(tid, "workspace", ""), Error, "No command /workspace", ); await h.berry.configureExtension(tid, "environment", { cwd: nested }); assertEquals(await offered(), true); await h.berry.configureExtension(tid, "environment", { cwd: secondary }); assertEquals(await offered(), true); await h.berry.command(tid, "cd", root.path); assertEquals(await offered(), false); await h.berry.configureExtension(tid, "environment", { cwd: repo, environment: "no-jj" }); assertEquals(await offered(), false); await h.berry.configureExtension(tid, "environment", { environment: "local", cwd: nested }); assertEquals(await offered(), true); offline = true; assertEquals(await offered(), false); await assertRejects( () => h.berry.command(tid, "workspace", ""), Error, "No command /workspace", ); assert((await h.berry.commands(tid)).some(({ name }) => name === "cd")); }, }); Deno.test("user shell commands choose PowerShell on the target Windows machine", async () => { await using root = await tempDir(); await using h = await setup({ open: (name, cwd) => Promise.resolve(Object.assign(localEnvironment(cwd ?? root.path), { name, platform: "win32", })), close: () => Promise.resolve(), }); await h.berry.install(defineExtension({ name: "code", initial: () => null, setup() { return (conv) => { if (conv.env?.platform !== "win32") return; conv.tool({ name: "powershell", description: "PowerShell", expose: "codemode", parameters: Type.Object({ command: Type.String() }), execute: ({ command }, { env }) => ({ shell: "powershell", command, cwd: env.cwd }), }); }; }, })); const tid = await h.berry.create({ extensions: { code: {} }, model: h.model }); const task = await h.berry.shell(tid, "Get-Location"); await h.berry.harness.waitForTask(task, BACKGROUND_CONTEXT); const conversation = (await h.berry.conversation(tid))!; await conversation.waitForIdle(BACKGROUND_CONTEXT); const entries = await conversation.entries( { order: "ascending" }, 100, undefined, BACKGROUND_CONTEXT, ); assertEquals(entries.items.find(ShellEntry.is)?.data.result, { shell: "powershell", command: "Get-Location", cwd: root.path, }); });