feat(pi-permission-ai-judge): add deferring authorizer extension

- add pnpm workspace scaffold with shared TypeScript config
- register an ai-bash-judge authorizer with pi-permission-system
- log permission request details and deterministic policy verdicts
- record review entries and defer to the next authorizer
This commit is contained in:
2026-08-08 19:43:21 +08:00
parent 035aa392c6
commit f62a073de7
8 changed files with 2741 additions and 0 deletions
@@ -0,0 +1,123 @@
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import {
getPermissionsService,
PERMISSIONS_READY_CHANNEL,
} from "@gotgenes/pi-permission-system";
const LINK_NAME = "ai-bash-judge";
export default function permissionAiJudge(pi: ExtensionAPI): void {
let sessionStarted = false;
let disposeAuthorizer: (() => void) | undefined;
/**
* 尝试向 pi-permission-system 注册我们的 Authorizer。
*
* 之所以不能只在 session_start 里注册,是因为:
* - 可能我们的 extension 先启动;
* - 也可能 pi-permission-system 先启动。
*
* 所以同时监听 session_start 和 permissions:ready,
* 谁后满足条件,谁完成注册。
*/
function tryRegister(): void {
if (!sessionStarted || disposeAuthorizer) {
return;
}
const service = getPermissionsService();
if (!service) {
console.debug(
`[${LINK_NAME}] permission service not ready; waiting`,
);
return;
}
disposeAuthorizer = service.registerAuthorizer(
LINK_NAME,
async (details, query, log) => {
const surface =
details.accessIntent?.surface ??
details.surface ??
undefined;
console.error(`[${LINK_NAME}] permission ask received`, {
requestId: details.requestId,
surface,
toolName: details.toolName,
command: details.command,
path: details.path,
value: details.value,
agentName: details.agentName,
});
/**
* 测试 PermissionQuery。
*
* 这不会再次触发 Authorizer。
* 它只是询问 pi-permission-system 的确定性规则:
* “如果检查这个 bash command,规则本身会怎么判?”
*/
if (surface === "bash" && details.command) {
const result = query.checkPermission(
"bash",
details.command,
details.agentName ?? undefined,
);
console.error(
`[${LINK_NAME}] deterministic policy says`,
result,
);
}
/**
* 写入 permission-system 自己的 review log。
*
* 以后 AI 的 decision trail 也应该写这里。
*/
log.review("ai_bash_judge.test", {
requestId: details.requestId,
surface,
command: details.command ?? null,
verdict: "defer",
});
/**
* 第一版永远不审批。
*
* defer = 我不知道 / 我不处理,
* 请 Authorizer Chain 继续交给下一个审批者。
*
* 正常情况下最终就是 LocalUserAuthorizer,
* 所以你还是会看到原来的 permission prompt。
*/
return {
kind: "defer",
};
},
);
console.error(`[${LINK_NAME}] registered`);
}
pi.on("session_start", () => {
sessionStarted = true;
tryRegister();
});
pi.events.on(PERMISSIONS_READY_CHANNEL, () => {
tryRegister();
});
pi.on("session_shutdown", () => {
disposeAuthorizer?.();
disposeAuthorizer = undefined;
sessionStarted = false;
console.error(`[${LINK_NAME}] unregistered`);
});
}