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
@@ -2,10 +2,8 @@ import { readFileSync } from "node:fs";
import { join } from "node:path";
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
import { afterEach, describe, expect, test, vi } from "vitest";
import type { Authorizer } from "#src/authority/authorizer";
import { AuthorizerRegistry } from "#src/authority/authorizer-registry";
import { AuthorizerSelection } from "#src/authority/authorizer-selection";
import { encloseInDelegationEnvelope } from "#src/authority/delegation-envelope";
import { ForwardedRequestServer } from "#src/authority/forwarded-request-server";
import type { PermissionPromptDecision } from "#src/authority/permission-dialog";
import type {
@@ -16,7 +14,6 @@ import {
PermissionPrompter,
type PromptPermissionDetails,
} from "#src/authority/permission-prompter";
import type { PermissionQuery } from "#src/service";
import {
makeAuthorizerSelectionDeps,
registerLink,
@@ -57,9 +54,8 @@ function readResponse(
* An approving `AskEscalator` that records the details it was handed.
*
* The reconstructed `PromptPermissionDetails` is itself the subject of the
* access-facts and bounded-delegation cases below: they assert its exact shape,
* and hand it to the real delegation envelope — a collaborator the server never
* touches, but one that reads the details the server builds.
* access-facts cases below: they assert the exact evidence passed to the
* serving node's configured authorizer chain.
*/
function makeCapturingEscalator() {
const escalated: PromptPermissionDetails[] = [];
@@ -550,9 +546,8 @@ describe("processInbox — child-fixed access facts on the escalated ask", () =>
value: "git push",
});
// Absence, not an explicit `undefined`: the delegation envelope's
// `accessIntent?.surface ?? surface` fallback reads the display surface only
// when the key is genuinely absent.
// Version-skew requests can omit access intent entirely; preserve that
// absence instead of manufacturing evidence.
expect(details).not.toHaveProperty("accessIntent");
});
});
@@ -640,49 +635,6 @@ describe("processInbox — the child's payload on the escalated ask", () => {
});
});
describe("processInbox — bounded delegation over forwarded asks", () => {
const query: PermissionQuery = {
checkPermission: vi.fn(),
getToolPermission: vi.fn(),
};
const log = makeAuthorizerLog();
const allowingLink: Authorizer["authorize"] = () =>
Promise.resolve({ kind: "allow" });
test("caps a link's allow on a forwarded path ask to defer", async () => {
const details = await escalateForwardedAsk({
id: "req-envelope-path",
source: "tool_call",
surface: "write",
value: "/worktree/issue-42/.ssh/config",
accessIntent: makeForwardedAccessIntent({
surface: "path",
matchValues: ["/worktree/issue-42/.ssh/config"],
boundaryValue: "/worktree/issue-42/.ssh/config",
}),
});
const enclosed = encloseInDelegationEnvelope(allowingLink);
// The gate surface, not the displayed tool name, decides exclusion — so a
// forwarded path ask is capped exactly like the same ask made locally.
expect(await enclosed(details, query, log)).toEqual({ kind: "defer" });
});
test("passes a link's allow on a forwarded bash ask through", async () => {
const details = await escalateForwardedAsk({
id: "req-envelope-bash",
source: "tool_call",
surface: "bash",
value: "npm test",
accessIntent: makeForwardedAccessIntent({ matchValues: ["npm test"] }),
});
const enclosed = encloseInDelegationEnvelope(allowingLink);
expect(await enclosed(details, query, log)).toEqual({ kind: "allow" });
});
});
describe("processInbox — the serving node's chain adjudicates a forwarded ask", () => {
/** A serving node with UI: its terminal is the human prompt. */