refactor: extract execution permission policy

This commit is contained in:
kris
2026-04-02 22:46:41 +08:00
parent 384dd570de
commit a3a4f3e980
8 changed files with 204 additions and 39 deletions

View File

@@ -0,0 +1,104 @@
import { listExecutionTools, type ExecutionToolName } from "@/lib/execution/tool-registry";
type CollaborationMode = "development" | "approval_required";
type ApprovalState = "not_required" | "pending_agent" | "pending_user" | "approved" | "rejected";
type PermissionPolicyProject = {
id: string;
isGroup: boolean;
collaborationMode: CollaborationMode;
approvalState: ApprovalState;
};
type ToolPolicy = {
allowedTools: ExecutionToolName[];
deniedTools: ExecutionToolName[];
};
type CollaborationPolicy = {
mode: CollaborationMode;
canDispatchDirectly: boolean;
canCrossThreadTalk: boolean;
};
type PermissionPolicyResult = {
allowed: boolean;
requiresApproval: boolean;
reason?: string;
toolPolicy: ToolPolicy;
collaborationPolicy: CollaborationPolicy;
};
const DEFAULT_APPROVAL_REQUIRED_REASON =
"当前还有一条主 Agent 推荐等待确认,请先确认或拒绝后再继续发送新指令。";
const EMPTY_TOOL_POLICY: ToolPolicy = {
allowedTools: [],
deniedTools: [],
};
function listDefaultAllowedTools(): ExecutionToolName[] {
return listExecutionTools()
.filter((tool) => tool.kind === "execution")
.map((tool) => tool.name);
}
function buildCollaborationPolicy(mode: CollaborationMode): CollaborationPolicy {
return {
mode,
canDispatchDirectly: mode === "development",
canCrossThreadTalk: mode === "development",
};
}
function buildAllowedPolicy(mode: CollaborationMode, requiresApproval: boolean): PermissionPolicyResult {
return {
allowed: true,
requiresApproval,
toolPolicy: {
allowedTools:
mode === "approval_required"
? ["group_dispatch_plan"]
: listDefaultAllowedTools(),
deniedTools: [],
},
collaborationPolicy: buildCollaborationPolicy(mode),
};
}
export function evaluatePermissionPolicy(input: {
project?: PermissionPolicyProject;
hasPendingDispatchPlan?: boolean;
}): PermissionPolicyResult {
const project = input.project;
if (!project) {
return {
allowed: true,
requiresApproval: false,
toolPolicy: EMPTY_TOOL_POLICY,
collaborationPolicy: buildCollaborationPolicy("development"),
};
}
if (project.isGroup && project.collaborationMode === "approval_required" && input.hasPendingDispatchPlan) {
return {
allowed: false,
requiresApproval: true,
reason: DEFAULT_APPROVAL_REQUIRED_REASON,
toolPolicy: {
allowedTools: ["group_dispatch_plan"],
deniedTools: ["dispatch_execution"],
},
collaborationPolicy: buildCollaborationPolicy("approval_required"),
};
}
return buildAllowedPolicy(
project.collaborationMode,
project.isGroup && project.collaborationMode === "approval_required",
);
}
export const evaluatePermissionPolicyForTesting = evaluatePermissionPolicy;

View File

@@ -0,0 +1,23 @@
export type ExecutionToolKind = "execution" | "analysis";
export type ExecutionToolName =
| "conversation_reply"
| "group_dispatch_plan"
| "dispatch_execution"
| "attachment_analysis";
export interface ExecutionToolDefinition {
name: ExecutionToolName;
kind: ExecutionToolKind;
}
const EXECUTION_TOOLS: readonly ExecutionToolDefinition[] = [
{ name: "conversation_reply", kind: "execution" },
{ name: "group_dispatch_plan", kind: "execution" },
{ name: "dispatch_execution", kind: "execution" },
{ name: "attachment_analysis", kind: "analysis" },
] as const;
export function listExecutionTools() {
return EXECUTION_TOOLS;
}