refactor(permission-system): simplify policy and autoreview flow

This commit is contained in:
云服务部-叶林立
2026-09-03 02:01:06 +08:00
parent 180da95ab7
commit 6e7effa792
112 changed files with 911 additions and 10346 deletions
@@ -136,76 +136,6 @@ describe("renderPolicyDenial", () => {
).toBe("[pi-permission-system] Denied by policy: 'bash'.");
});
it("names the escaped boundary for a tool external-directory deny", () => {
expect(
renderPolicyDenial(
payload(
"external_directory",
{
surface: "external_directory",
toolName: "write",
value: "/etc/hosts",
matchedPattern: "*",
},
[{ label: "working directory", text: "/repo", detail: null }],
),
null,
),
).toBe(
"[pi-permission-system] Denied by policy: 'external_directory' for tool 'write' for path '/etc/hosts' (rule '*'): outside working directory '/repo'.",
);
});
it("names the canonical target when a path resolves elsewhere", () => {
expect(
renderPolicyDenial(
payload(
"external_directory",
{
surface: "external_directory",
toolName: "read",
value: "link",
matchedPattern: "*",
},
[
{ label: "resolves to", text: "/etc/shadow", detail: null },
{ label: "working directory", text: "/repo", detail: null },
],
),
null,
),
).toBe(
"[pi-permission-system] Denied by policy: 'external_directory' for tool 'read' for path 'link' (resolves to '/etc/shadow') (rule '*'): outside working directory '/repo'.",
);
});
it("names every escaping path but not the command for a bash external-directory deny", () => {
expect(
renderPolicyDenial(
payload(
"bash_external_directory",
{
surface: "external_directory",
toolName: "bash",
value: "diff /etc/hosts ~/.ssh/config",
matchedPattern: "*",
},
[
{ label: "working directory", text: "/repo", detail: null },
{ label: "external path", text: "/etc/hosts", detail: null },
{
label: "external path",
text: "~/.ssh/config",
detail: "/home/me/.ssh/config",
},
],
),
null,
),
).toBe(
"[pi-permission-system] Denied by policy: 'external_directory' for tool 'bash' for paths '/etc/hosts', '~/.ssh/config' (resolves to '/home/me/.ssh/config') (rule '*'): outside working directory '/repo'.",
);
});
it("names the MCP target", () => {
expect(
@@ -370,25 +300,6 @@ describe("renderUnavailableDenial", () => {
);
});
it("omits the escaped boundary, which no retry shape would change", () => {
expect(
renderUnavailableDenial(
payload(
"external_directory",
{
surface: "external_directory",
toolName: "write",
value: "/etc/hosts",
matchedPattern: "*",
},
[{ label: "working directory", text: "/repo", detail: null }],
),
null,
),
).toBe(
"[pi-permission-system] This 'external_directory' call for tool 'write' for path '/etc/hosts' (rule '*') requires approval, but no interactive UI is available.",
);
});
});
describe("the flagged-element field cap", () => {
@@ -48,24 +48,6 @@ describe("renderPromptDialog", () => {
]);
});
it("names the gate surface when it differs from the tool", () => {
expect(
render({
kind: "external_directory",
request: requestFacts({
surface: "external_directory",
toolName: "write",
value: "/etc/hosts",
matchedPattern: "*",
}),
}),
).toEqual([
"tool : write",
"surface : external_directory",
"rule : *",
"path : /etc/hosts",
]);
});
it("omits an agent line for an unnamed local requester, and a surface the value label already names", () => {
expect(
@@ -214,36 +196,6 @@ describe("renderPromptDialog", () => {
]);
});
it("keeps an entry's detail on the entry's own line", () => {
expect(
render({
kind: "bash_external_directory",
request: requestFacts({
surface: "external_directory",
toolName: "bash",
value: "cat /etc/hosts /tmp/x",
matchedPattern: "*",
}),
evidence: [
{ label: "working directory", text: "/repo", detail: null },
{
label: "external path",
text: "/etc/hosts",
detail: "/private/etc/hosts",
},
{ label: "external path", text: "/tmp/x", detail: null },
],
}),
).toEqual([
"tool : bash",
"surface : external_directory",
"rule : *",
"command : cat /etc/hosts /tmp/x",
"working directory : /repo",
"external path : /etc/hosts → /private/etc/hosts",
"external path : /tmp/x",
]);
});
it("renders a skill read's path", () => {
expect(
@@ -558,35 +510,6 @@ describe("renderPromptDialog", () => {
]);
});
it("paints each escaping path rather than the command that referenced them", () => {
expect(
renderPromptDialog(
makePromptPayload({
kind: "bash_external_directory",
request: {
...makePromptPayload().request,
surface: "external_directory",
toolName: "bash",
value: "cat /etc/hosts /etc/hostsbackup",
matchedPattern: "*",
},
evidence: [
{ label: "working directory", text: "/repo", detail: null },
{ label: "external path", text: "/etc/hosts", detail: null },
],
}),
completeViewBudget(200),
mark,
).lines,
).toEqual([
"tool : bash",
"surface : external_directory",
"rule : *",
"command : cat [/etc/hosts] /etc/hostsbackup",
"working directory : /repo",
"external path : [/etc/hosts]",
]);
});
it("leaves the text untouched when no paint is supplied", () => {
expect(
@@ -26,10 +26,6 @@ function payloadOf(
};
}
/** An `external path` evidence entry, as the bash external-directory gate emits it. */
function externalPath(path: string, resolved: string | null): PromptEvidence {
return { label: "external path", text: path, detail: resolved };
}
describe("flaggedElements", () => {
it("flags the decision-relevant value for a single-value ask", () => {
@@ -44,17 +40,6 @@ describe("flaggedElements", () => {
]);
});
it("flags the escaping paths rather than the command for a bash external-directory ask", () => {
expect(
flaggedElements(
payloadOf("bash_external_directory", "diff /etc/hosts ~/.ssh/config", [
{ label: "working directory", text: "/repo", detail: null },
externalPath("/etc/hosts", null),
externalPath("~/.ssh/config", "/home/me/.ssh/config"),
]),
),
).toEqual(["/etc/hosts", "~/.ssh/config"]);
});
it("flags nothing when the value is empty", () => {
expect(flaggedElements(payloadOf("tool", ""))).toEqual([]);
@@ -64,11 +49,9 @@ describe("flaggedElements", () => {
describe("valueLabel", () => {
it.each([
["bash", "command"],
["bash_external_directory", "command"],
["mcp", "target"],
["tool", "tool"],
["path", "path"],
["external_directory", "path"],
["skill", "skill"],
["skill_read", "skill"],
] as const)("labels a %s ask's value %s", (kind, label) => {
@@ -88,11 +71,6 @@ describe("valueLabel", () => {
});
describe("flaggedElementLabel", () => {
it("labels the escaping paths a bash external-directory ask flags", () => {
expect(
flaggedElementLabel(payloadOf("bash_external_directory", "cmd")),
).toBe("path");
});
it.each([
"bash",
@@ -1,9 +1,5 @@
import { describe, expect, test } from "vitest";
import {
buildBashExternalDirectoryAskPayload,
buildExternalDirectoryAskPayload,
buildPathAskPayload,
} from "#src/presentation/path-ask-payload";
import { buildPathAskPayload } from "#src/presentation/path-ask-payload";
describe("buildPathAskPayload", () => {
test("carries the typed path as the decision value and the rule that fired", () => {
@@ -38,89 +34,3 @@ describe("buildPathAskPayload", () => {
).toEqual({ agentName: null, forwarded: false, sessionId: null });
});
});
describe("buildExternalDirectoryAskPayload", () => {
test("carries the typed path, the boundary, and the requester", () => {
const payload = buildExternalDirectoryAskPayload({
toolName: "write",
pathValue: "/tmp/out.txt",
cwd: "/projects/my-app",
agentName: "my-agent",
});
expect(payload.kind).toBe("external_directory");
expect(payload.request.toolName).toBe("write");
expect(payload.request.value).toBe("/tmp/out.txt");
expect(payload.request.requester.agentName).toBe("my-agent");
expect(payload.evidence).toEqual([
{ label: "working directory", text: "/projects/my-app", detail: null },
]);
});
test("discloses the resolved path as its own entry when it differs", () => {
expect(
buildExternalDirectoryAskPayload({
toolName: "read",
pathValue: "demo-symlink-passwd",
resolvedPath: "/etc/passwd",
cwd: "/projects/my-app",
agentName: null,
}).evidence,
).toEqual([
{ label: "resolves to", text: "/etc/passwd", detail: null },
{ label: "working directory", text: "/projects/my-app", detail: null },
]);
});
test("omits the disclosure when resolvedPath is undefined", () => {
const payload = buildExternalDirectoryAskPayload({
toolName: "read",
pathValue: "/etc/passwd",
cwd: "/projects/my-app",
agentName: null,
});
expect(payload.evidence).toEqual([
{ label: "working directory", text: "/projects/my-app", detail: null },
]);
});
});
describe("buildBashExternalDirectoryAskPayload", () => {
test("makes the command the value and the paths it reached evidence", () => {
const payload = buildBashExternalDirectoryAskPayload({
command: "cat /etc/passwd",
externalPaths: [{ path: "/etc/passwd" }],
cwd: "/projects/my-app",
agentName: "my-agent",
toolName: "bash",
});
expect(payload.kind).toBe("bash_external_directory");
expect(payload.request.value).toBe("cat /etc/passwd");
expect(payload.request.requester.agentName).toBe("my-agent");
expect(payload.evidence).toEqual([
{ label: "working directory", text: "/projects/my-app", detail: null },
{ label: "external path", text: "/etc/passwd", detail: null },
]);
});
test("binds each path's canonical alias to its own entry", () => {
const payload = buildBashExternalDirectoryAskPayload({
command: "cat a b",
externalPaths: [
{ path: "/a", resolvedPath: "/private/a" },
{ path: "/b" },
],
cwd: "/repo",
agentName: null,
toolName: "bash",
});
expect(payload.evidence).toEqual([
{ label: "working directory", text: "/repo", detail: null },
{ label: "external path", text: "/a", detail: "/private/a" },
{ label: "external path", text: "/b", detail: null },
]);
});
});