Menu
popagent
publicLatest change b4f0c91e83b9048b4861ed58f43a0ab71b000d61 - Provide devices in contained workspaces by Ólafur Búi Ólafsson
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { resolve } from "node:path";
import { createWorkspaceTools, WORKSPACE_TOOLS } from "@mastra/core/workspace";
import { agentWorkspace } from "./workspace";
const testFile = `workspace-test-${crypto.randomUUID()}.txt`;
const lspSource = `workspace-lsp-${crypto.randomUUID()}.ts`;
const lspConsumer = `workspace-lsp-${crypto.randomUUID()}.test.ts`;
const workspaceRoot = resolve(
process.env.POPAGENT_WORKSPACE_ROOT ?? "workspace",
process.env.POPAGENT_DEFAULT_REPOSITORY ?? ".",
);
const filesystem = agentWorkspace.filesystem!;
const sandbox = agentWorkspace.sandbox!;
beforeAll(() => agentWorkspace.init());
afterAll(async () => {
await filesystem.deleteFile(testFile, { force: true });
await filesystem.deleteFile(lspSource, { force: true });
await filesystem.deleteFile(lspConsumer, { force: true });
await agentWorkspace.destroy();
});
describe("agentWorkspace", () => {
test("reads and writes files inside the contained workspace", async () => {
await filesystem.writeFile(testFile, "workspace file access");
expect(await filesystem.readFile(testFile, { encoding: "utf8" })).toBe("workspace file access");
expect(filesystem.basePath).toBe(workspaceRoot);
});
test("requires a fresh read before an agent overwrites a file", async () => {
await filesystem.writeFile(testFile, "original");
const tools = await createWorkspaceTools(agentWorkspace);
const readFile = tools[WORKSPACE_TOOLS.FILESYSTEM.READ_FILE]!;
const writeFile = tools[WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE]!;
await expect(writeFile.execute!({
path: testFile,
content: "unreviewed overwrite",
overwrite: true,
}, {} as never)).rejects.toThrow("must read a file before writing");
await readFile.execute!({ path: testFile }, {} as never);
await writeFile.execute!({
path: testFile,
content: "reviewed overwrite",
overwrite: true,
}, {} as never);
expect(await filesystem.readFile(testFile, { encoding: "utf8" }))
.toBe("reviewed overwrite");
await expect(writeFile.execute!({
path: testFile,
content: "stale second overwrite",
overwrite: true,
}, {} as never)).rejects.toThrow("must read a file before writing");
});
test("performs semantic LSP inspection with repository-relative paths", async () => {
await filesystem.writeFile(lspSource, "export const semanticValue = 42;\n");
await filesystem.writeFile(lspConsumer, `import { semanticValue } from "./${lspSource.slice(0, -3)}";\nconsole.log(semanticValue);\n`);
const tools = await createWorkspaceTools(agentWorkspace);
const inspect = tools[WORKSPACE_TOOLS.LSP.LSP_INSPECT]!;
const result = await inspect.execute!({
path: lspConsumer,
line: 2,
match: "console.log(<<<semanticValue);",
}, {} as never);
expect(result).toMatchObject({
hover: { value: expect.stringContaining("semanticValue") },
definition: expect.arrayContaining([
expect.objectContaining({ location: expect.stringContaining(lspConsumer) }),
]),
});
}, 30_000);
test("instructs agents to use contained repository-relative tool paths", () => {
expect(filesystem.getInstructions?.()).toContain(
"Use repository-relative paths for every workspace tool, including LSP inspection",
);
});
test("rejects filesystem paths outside the workspace", async () => {
await expect(filesystem.readFile("../package.json")).rejects.toThrow();
});
test("executes shell commands from the workspace", async () => {
const result = await sandbox.executeCommand?.("pwd");
expect(result?.success).toBe(true);
expect(result?.stdout.trim()).toBe(workspaceRoot);
const escaped = await sandbox.executeCommand?.("sh", ["-c", "test -e ../package.json"]);
expect(escaped?.exitCode).not.toBe(0);
});
test("provides standard device files to contained commands", async () => {
const result = await sandbox.executeCommand?.("git", ["status", "--short"]);
expect(result?.success).toBe(true);
});
});