section-capability-policy.tsx
1 'use client' 2 3 import type { SettingsSectionProps } from './types' 4 5 export function CapabilityPolicySection({ appSettings, patchSettings, inputClass }: SettingsSectionProps) { 6 return ( 7 <div className="mb-10"> 8 <h3 className="font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2"> 9 Capability Policy 10 </h3> 11 <p className="text-[12px] text-text-3 mb-5"> 12 Centralized guardrails for agent tool families and platform features. SwarmClaw now relies on direct capability policy and explicit feature gates rather than a workflow approval queue. 13 </p> 14 <div className="p-6 rounded-[18px] bg-surface border border-white/[0.06]"> 15 <label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-3">Policy Mode</label> 16 <div className="grid grid-cols-3 gap-2 mb-5"> 17 {([ 18 { id: 'permissive', name: 'Permissive' }, 19 { id: 'balanced', name: 'Balanced' }, 20 { id: 'strict', name: 'Strict' }, 21 ] as const).map((mode) => ( 22 <button 23 key={mode.id} 24 onClick={() => patchSettings({ capabilityPolicyMode: mode.id })} 25 className={`py-3 px-3 rounded-[12px] text-center cursor-pointer transition-all text-[13px] font-600 border 26 ${(appSettings.capabilityPolicyMode || 'permissive') === mode.id 27 ? 'bg-accent-soft border-accent-bright/25 text-accent-bright' 28 : 'bg-bg border-white/[0.06] text-text-2 hover:bg-surface-2'}`} 29 style={{ fontFamily: 'inherit' }} 30 > 31 {mode.name} 32 </button> 33 ))} 34 </div> 35 36 <div className="grid grid-cols-1 gap-4"> 37 <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> 38 <div className="rounded-[12px] border border-white/[0.06] bg-bg px-4 py-4"> 39 <div className="flex items-center justify-between gap-4"> 40 <div> 41 <div className="text-[12px] font-600 text-text-2">Task Management</div> 42 <p className="text-[11px] text-text-3/60 mt-1 leading-relaxed"> 43 Controls the task board and agent access to durable backlog tracking. Internal queue execution still works underneath. 44 </p> 45 </div> 46 <button 47 onClick={() => patchSettings({ taskManagementEnabled: !(appSettings.taskManagementEnabled ?? true) })} 48 className={`relative w-10 h-[22px] rounded-full transition-colors duration-200 cursor-pointer ${(appSettings.taskManagementEnabled ?? true) ? 'bg-accent' : 'bg-white/[0.12]'}`} 49 aria-label="Toggle task management" 50 > 51 <span className={`absolute top-[3px] left-[3px] w-4 h-4 rounded-full bg-white transition-transform duration-200 ${(appSettings.taskManagementEnabled ?? true) ? 'translate-x-[18px]' : ''}`} /> 52 </button> 53 </div> 54 </div> 55 56 <div className="rounded-[12px] border border-white/[0.06] bg-bg px-4 py-4"> 57 <div className="flex items-center justify-between gap-4"> 58 <div> 59 <div className="text-[12px] font-600 text-text-2">Project Management</div> 60 <p className="text-[11px] text-text-3/60 mt-1 leading-relaxed"> 61 Controls the project operating-system UI and agent access to durable project context for objectives, credentials, and heartbeat plans. 62 </p> 63 </div> 64 <button 65 onClick={() => patchSettings({ projectManagementEnabled: !(appSettings.projectManagementEnabled ?? true) })} 66 className={`relative w-10 h-[22px] rounded-full transition-colors duration-200 cursor-pointer ${(appSettings.projectManagementEnabled ?? true) ? 'bg-accent' : 'bg-white/[0.12]'}`} 67 aria-label="Toggle project management" 68 > 69 <span className={`absolute top-[3px] left-[3px] w-4 h-4 rounded-full bg-white transition-transform duration-200 ${(appSettings.projectManagementEnabled ?? true) ? 'translate-x-[18px]' : ''}`} /> 70 </button> 71 </div> 72 </div> 73 </div> 74 75 <div className="rounded-[12px] border border-white/[0.06] bg-bg px-4 py-4"> 76 <div className="flex items-center justify-between gap-4"> 77 <div> 78 <div className="text-[12px] font-600 text-text-2">Outbound Connector Confirmation</div> 79 <p className="text-[11px] text-text-3/60 mt-1 leading-relaxed"> 80 Require connector sends to pass an explicit `approved=true` confirmation boundary. This is a direct feature gate, not an approvals queue. 81 </p> 82 </div> 83 <button 84 onClick={() => patchSettings({ safetyRequireApprovalForOutbound: !(appSettings.safetyRequireApprovalForOutbound ?? false) })} 85 className={`inline-flex h-[22px] w-10 shrink-0 items-center rounded-full border border-white/[0.08] p-[3px] transition-colors duration-200 cursor-pointer ${ 86 (appSettings.safetyRequireApprovalForOutbound ?? false) ? 'justify-end bg-accent' : 'justify-start bg-white/[0.16]' 87 }`} 88 aria-label="Toggle outbound connector confirmation" 89 > 90 <span className="h-4 w-4 rounded-full bg-white shadow-[0_1px_4px_rgba(0,0,0,0.35)]" /> 91 </button> 92 </div> 93 </div> 94 95 <div> 96 <label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Blocked Categories</label> 97 <input 98 type="text" 99 value={(appSettings.capabilityBlockedCategories || []).join(', ')} 100 onChange={(e) => patchSettings({ 101 capabilityBlockedCategories: e.target.value 102 .split(',') 103 .map((part) => part.trim()) 104 .filter(Boolean), 105 })} 106 placeholder="execution, filesystem, platform, outbound" 107 className={inputClass} 108 style={{ fontFamily: 'inherit' }} 109 /> 110 <p className="text-[11px] text-text-3/60 mt-2">Supported categories: filesystem, execution, network, browser, memory, delegation, platform, outbound.</p> 111 </div> 112 113 <div> 114 <label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Blocked Tools</label> 115 <input 116 type="text" 117 value={(appSettings.capabilityBlockedTools || []).join(', ')} 118 onChange={(e) => patchSettings({ 119 capabilityBlockedTools: e.target.value 120 .split(',') 121 .map((part) => part.trim()) 122 .filter(Boolean), 123 })} 124 placeholder="delete_file, manage_connectors, delegate_to_codex_cli" 125 className={inputClass} 126 style={{ fontFamily: 'inherit' }} 127 /> 128 </div> 129 130 <div> 131 <label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Allowed Tools (Override)</label> 132 <input 133 type="text" 134 value={(appSettings.capabilityAllowedTools || []).join(', ')} 135 onChange={(e) => patchSettings({ 136 capabilityAllowedTools: e.target.value 137 .split(',') 138 .map((part) => part.trim()) 139 .filter(Boolean), 140 })} 141 placeholder="shell, web_fetch, browser" 142 className={inputClass} 143 style={{ fontFamily: 'inherit' }} 144 /> 145 <p className="text-[11px] text-text-3/60 mt-2">Use this to re-allow specific tool families when running in strict mode.</p> 146 </div> 147 148 </div> 149 </div> 150 </div> 151 ) 152 }