import { useEffect, useMemo, useState } from "react"; import type { AgentPermissions, TrustPreset } from "@paperclipai/shared"; import { Lock, ShieldAlert } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Field, CollapsibleSection } from "./agent-config-primitives"; import { buildPermissionsForTrustPreset, clearSingleLowTrustBoundaryTarget, getLowTrustBoundary, getSingleLowTrustBoundaryTarget, getTrustPreset, isCeLowTrustBoundaryEditable, lowTrustBoundaryHasScope, setSingleLowTrustBoundaryTarget, summarizeLowTrustBoundaryTarget, TRUST_PRESET_DESCRIPTIONS, TRUST_PRESET_LABELS, type LowTrustBoundaryTarget, } from "../lib/trust-policy-ui"; import { cn } from "../lib/utils"; const inputClass = "w-full rounded-md border border-border px-2.5 py-1.5 bg-transparent outline-none text-sm font-mono placeholder:text-muted-foreground/40"; function formatCount(value: readonly unknown[] | undefined, singular: string, plural: string) { const count = value?.length ?? 0; if (count === 0) return "-"; return `${count} ${count === 1 ? singular : plural}`; } function PolicyRow({ label, value }: { label: string; value: string }) { return (
{label} {value}
); } export interface LowTrustBoundaryCandidate { id: string; label: string; } type LowTrustBoundaryTargetType = LowTrustBoundaryTarget["type"]; const BOUNDARY_TARGET_LABELS: Record = { project: "Project", root_issue: "Root issue", issue: "Issue", }; export function TrustPresetSection({ permissions, onChange, disabled, companyId, projectCandidates = [], issueCandidates = [], candidatesLoading, }: { permissions: Partial | null | undefined; onChange: (permissions: Partial) => void; disabled?: boolean; companyId?: string | null; projectCandidates?: LowTrustBoundaryCandidate[]; issueCandidates?: LowTrustBoundaryCandidate[]; candidatesLoading?: boolean; }) { const [policyOpen, setPolicyOpen] = useState(false); const preset = getTrustPreset(permissions); const boundary = getLowTrustBoundary(permissions); const boundaryTarget = getSingleLowTrustBoundaryTarget(boundary); const [targetType, setTargetType] = useState(boundaryTarget?.type ?? "project"); const lowTrust = preset === "low_trust_review"; const hasScope = lowTrustBoundaryHasScope(boundary); const boundaryEditable = isCeLowTrustBoundaryEditable(boundary); const policy = permissions?.authorizationPolicy ?? null; const managedPermissions = useMemo( () => buildPermissionsForTrustPreset(permissions, preset), [permissions, preset], ); useEffect(() => { if (boundaryTarget) setTargetType(boundaryTarget.type); }, [boundaryTarget?.type]); function handlePresetChange(value: string) { const nextPreset: TrustPreset = value === "low_trust_review" ? "low_trust_review" : "standard"; onChange(buildPermissionsForTrustPreset(permissions, nextPreset)); } function handleBoundaryTargetChange(targetId: string) { if (!companyId || !targetId) return; onChange(setSingleLowTrustBoundaryTarget(permissions, companyId, { type: targetType, id: targetId })); } function handleClearBoundary() { onChange(clearSingleLowTrustBoundaryTarget(permissions)); } const targetCandidates = targetType === "project" ? projectCandidates : issueCandidates; const boundaryValue = boundaryTarget?.type === targetType ? boundaryTarget.id : ""; return (

Trust

{TRUST_PRESET_DESCRIPTIONS[preset]}

{lowTrust ? (
{hasScope ? ( ) : ( )}

{hasScope ? "Containment active" : "Containment not configured"}

{hasScope ? "This agent can only read and mutate work inside its assigned review boundary. Raw output is quarantined from higher-trust agents until a trusted reviewer promotes it." : "This agent is set to low-trust review, but no project, root issue, or issue scope is set in the core policy. Add a scope before this agent can run without denial."}

{boundaryEditable ? (

CE saves one containment boundary at a time. Saved policies include this company id.

{boundaryTarget ? ( ) : null}
) : (

Managed by EE/API

This policy has {summarizeLowTrustBoundaryTarget(boundary).toLowerCase()} and cannot be edited by the CE single-boundary editor.

)}

Want to set more than one containment boundary?{" "} Get Paperclip EE.

setPolicyOpen((open) => !open)} >
!["trustPreset", "reviewPreset", "trustBoundary"].includes(key)) ? "Custom advanced policy fields preserved" : "-"} />
) : null} {managedPermissions.authorizationPolicy?.reviewPreset ? null : (

Advanced permissions remain editable through the EE permissions extension when installed.

)}
); }