54 lines
3.4 KiB
TypeScript
54 lines
3.4 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { spawn } from "node:child_process";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
import type { RequestPermissionRequest } from "@agentclientprotocol/sdk";
|
|
import { AcpClient, decidePermission } from "../src/acp/client.js";
|
|
|
|
const request = (rawInput: unknown, overrides: Partial<RequestPermissionRequest["toolCall"]> = {}): RequestPermissionRequest => ({
|
|
sessionId: "session",
|
|
toolCall: { toolCallId: "call", title: "bash git status", kind: "execute", name: "bash", rawInput, ...overrides },
|
|
options: [
|
|
{ optionId: "allow", name: "Allow", kind: "allow_once" },
|
|
{ optionId: "reject", name: "Reject", kind: "reject_once" }
|
|
]
|
|
});
|
|
|
|
test("permission deny and allowlist fail closed", () => {
|
|
assert.equal(decidePermission(request("git status"), { mode: "deny", allowedTools: [], allowedCommandPatterns: [] }).outcome.outcome, "selected");
|
|
const allowed = decidePermission(request("git status"), { mode: "allowlist", allowedTools: ["bash"], allowedCommandPatterns: ["^git status$"] });
|
|
assert.deepEqual(allowed.outcome, { outcome: "selected", optionId: "allow" });
|
|
const objectInput = decidePermission(request({ command: "git status", timeout: 60 }), { mode: "allowlist", allowedTools: ["bash"], allowedCommandPatterns: ["^git status$"] });
|
|
assert.deepEqual(objectInput.outcome, { outcome: "selected", optionId: "allow" });
|
|
const missingDetail = decidePermission(request(undefined), { mode: "allowlist", allowedTools: ["bash"], allowedCommandPatterns: ["^git status$"] });
|
|
assert.deepEqual(missingDetail.outcome, { outcome: "selected", optionId: "reject" });
|
|
const destructive = decidePermission(request("rm -rf /"), { mode: "allowlist", allowedTools: ["bash"], allowedCommandPatterns: ["^git status$"] });
|
|
assert.deepEqual(destructive.outcome, { outcome: "selected", optionId: "reject" });
|
|
|
|
const unanchored = { mode: "allowlist" as const, allowedTools: ["bash"], allowedCommandPatterns: ["git status"] };
|
|
assert.deepEqual(decidePermission(request("git status"), unanchored).outcome, { outcome: "selected", optionId: "allow" });
|
|
assert.deepEqual(decidePermission(request("git status; rm -rf /"), unanchored).outcome, { outcome: "selected", optionId: "reject" });
|
|
assert.deepEqual(decidePermission(request("git status", { name: "python", title: "bash git status" }), unanchored).outcome, { outcome: "selected", optionId: "reject" });
|
|
assert.deepEqual(decidePermission(request("git status", { name: undefined, title: "bash git status" }), unanchored).outcome, { outcome: "selected", optionId: "reject" });
|
|
assert.deepEqual(decidePermission(request({ command: "git status", env: { PATH: "/tmp" } }), unanchored).outcome, { outcome: "selected", optionId: "reject" });
|
|
});
|
|
|
|
test("reports activity for every update from the active session", async () => {
|
|
const child = spawn(process.execPath, [path.resolve("test/fixtures/fake-acp-agent.mjs")], { stdio: ["pipe", "pipe", "pipe"] });
|
|
let activities = 0;
|
|
const client = new AcpClient(child, {
|
|
initializeTimeoutMs: 1_000,
|
|
policy: { mode: "deny", allowedTools: [], allowedCommandPatterns: [] },
|
|
onSessionActivity: () => { activities++; }
|
|
});
|
|
try {
|
|
await client.initialize();
|
|
await client.newSession(path.resolve("."));
|
|
assert.equal(await client.prompt("activity"), "firstsecond");
|
|
assert.equal(activities, 2);
|
|
} finally {
|
|
client.close();
|
|
child.kill("SIGTERM");
|
|
}
|
|
});
|