/ src / views / settings / section-capability-policy.tsx
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  }