mirror of
https://github.com/SikongJueluo/pi-extensions.git
synced 2026-10-05 11:52:55 +08:00
- 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
124 lines
3.8 KiB
TypeScript
124 lines
3.8 KiB
TypeScript
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`);
|
||
});
|
||
}
|