import "./AgentPermissionPolicyEditor.css"; import { AGENT_PERMISSION_POLICY_ACTION_CATEGORIES, AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES, AGENT_PERMISSION_POLICY_EXEMPT_TOOL_EXAMPLES, type AgentPermissionPolicy, type AgentPermissionPolicyDisposition, type AgentPermissionPolicyRules, } from "@fusion/core"; type Mode = "project-default" | "agent-override"; interface Props { value: AgentPermissionPolicy | undefined; projectDefault?: Partial; mode: Mode; onChange(next: AgentPermissionPolicy | undefined): void; disabled?: boolean; } const DISPOSITIONS: AgentPermissionPolicyDisposition[] = ["allow", "require-approval", "block"]; const CATEGORY_LABELS: Record = { git_write: { label: "Git writes", description: "Commits, branch updates, and merge-affecting git changes." }, file_write_delete: { label: "File writes/deletes", description: "Create, edit, or remove files in the workspace." }, command_execution: { label: "Command execution", description: "Runs shell commands and scripts." }, network_api: { label: "Network/API", description: "Outbound network or API access." }, task_agent_mutation: { label: "Task/agent mutation", description: "Task state changes, delegation, or agent lifecycle actions." }, }; function buildAllowRules(): AgentPermissionPolicyRules { return { git_write: "allow", file_write_delete: "allow", command_execution: "allow", network_api: "allow", task_agent_mutation: "allow", }; } const PRESET_RULES: Record<"unrestricted" | "approval-required" | "locked-down", AgentPermissionPolicyRules> = { unrestricted: { git_write: "allow", file_write_delete: "allow", command_execution: "allow", network_api: "allow", task_agent_mutation: "allow", }, "approval-required": { git_write: "require-approval", file_write_delete: "require-approval", command_execution: "require-approval", network_api: "require-approval", task_agent_mutation: "require-approval", }, "locked-down": { git_write: "block", file_write_delete: "block", command_execution: "block", network_api: "block", task_agent_mutation: "block", }, }; function getPresetRules(presetId: "unrestricted" | "approval-required" | "locked-down"): AgentPermissionPolicyRules { return PRESET_RULES[presetId]; } function matchesRules(a: AgentPermissionPolicyRules, b: AgentPermissionPolicyRules): boolean { return AGENT_PERMISSION_POLICY_ACTION_CATEGORIES.every((category) => a[category] === b[category]); } function derivePresetFromRules(rules: AgentPermissionPolicyRules): AgentPermissionPolicy["presetId"] { if (matchesRules(rules, PRESET_RULES.unrestricted)) return "unrestricted"; if (matchesRules(rules, PRESET_RULES["approval-required"])) return "approval-required"; if (matchesRules(rules, PRESET_RULES["locked-down"])) return "locked-down"; return "custom"; } export function AgentPermissionPolicyEditor({ value, projectDefault, mode, onChange, disabled = false }: Props) { const derivedPreset = value ? derivePresetFromRules(value.rules) : "unrestricted"; const currentPreset = mode === "agent-override" && !value ? "inherit" : (value?.presetId === "custom" ? derivedPreset : (value?.presetId ?? "unrestricted")); const rules = value?.rules ?? buildAllowRules(); const setPreset = (preset: string) => { if (mode === "agent-override" && preset === "inherit") { onChange(undefined); return; } if (preset === "custom") { onChange({ presetId: "custom", rules: { ...rules } }); return; } onChange({ presetId: preset as AgentPermissionPolicy["presetId"], rules: getPresetRules(preset as "unrestricted" | "approval-required" | "locked-down") }); }; const setRule = (category: keyof AgentPermissionPolicyRules, next: string) => { if (mode === "agent-override" && next === "inherit") { if (!value) return; const nextRules = { ...value.rules }; nextRules[category] = projectDefault?.[category] ?? "allow"; onChange({ presetId: "custom", rules: nextRules }); return; } const nextRules = { ...rules, [category]: next as AgentPermissionPolicyDisposition }; onChange({ presetId: "custom", rules: nextRules }); }; return (
{AGENT_PERMISSION_POLICY_ACTION_CATEGORIES.map((category) => { const meta = CATEGORY_LABELS[category] ?? { label: category, description: "" }; const inherited = projectDefault?.[category] ?? "allow"; const effective = rules[category] ?? "allow"; const rowValue = mode === "agent-override" && !value ? "inherit" : effective; return (
{meta.label}
{meta.description}
    {(AGENT_PERMISSION_POLICY_CATEGORY_TOOL_EXAMPLES[category] ?? []).map((toolName) => (
  • {toolName}
  • ))}
{mode === "agent-override" && rowValue === "inherit" ? (
from project default: {inherited === "require-approval" ? "Require approval" : inherited}
) : null}
); })}
Tools exempt from approval policy

These coordination tools bypass approval policy so heartbeats and inter-agent messaging cannot deadlock. They are not user-configurable.

    {AGENT_PERMISSION_POLICY_EXEMPT_TOOL_EXAMPLES.map((toolName) => (
  • {toolName}
  • ))}
); }