// frontend/components/AdminTabs/AdvancedTab.jsx import React, { useEffect, useState, useCallback } from "react"; import { apiUrl, safeFetchJSON } from "../../utils/api.js"; /** * Advanced tab — inline toggles for: * - Lite Mode (via /api/settings/topology — sets topology to "lite_mode") * - Permission Mode (normal | auto | plan via /api/permissions/mode) * - Link to full Settings modal for power users * * Best practices applied: * - Optimistic UI with rollback on error * - Each setting has its own loading indicator (no global lock) * - Descriptions explain what each mode does * - ARIA-labeled toggle switches for accessibility */ const PERMISSION_MODES = [ { value: "normal", label: "Normal", description: "Ask before writing files or running commands (recommended).", }, { value: "auto", label: "Auto", description: "Approve all tool calls automatically. Use only when you trust the agent.", }, { value: "plan", label: "Plan Only", description: "Read-only mode. Agent cannot write files or run commands.", }, ]; function ToggleSwitch({ checked, onChange, disabled, ariaLabel }) { return ( ); } export default function AdvancedTab({ showToast, onOpenFullSettings }) { const [liteMode, setLiteMode] = useState(false); const [permissionMode, setPermissionMode] = useState("normal"); const [loading, setLoading] = useState(true); const [updatingLite, setUpdatingLite] = useState(false); const [updatingPerm, setUpdatingPerm] = useState(false); const [error, setError] = useState(null); // Initial fetch: topology preference + permission mode useEffect(() => { let cancelled = false; (async () => { try { const [topo, perms] = await Promise.all([ safeFetchJSON(apiUrl("/api/settings/topology"), { timeout: 5000 }) .catch(() => ({ topology: null })), safeFetchJSON(apiUrl("/api/permissions"), { timeout: 5000 }) .catch(() => ({ mode: "normal" })), ]); if (cancelled) return; setLiteMode(topo?.topology === "lite_mode"); setPermissionMode(perms?.mode || perms?.policy?.mode || "normal"); } catch (err) { if (!cancelled) setError(err?.message || "Failed to load settings"); } finally { if (!cancelled) setLoading(false); } })(); return () => { cancelled = true; }; }, []); const handleLiteToggle = useCallback(async (next) => { setUpdatingLite(true); setError(null); const previous = liteMode; setLiteMode(next); // optimistic try { await safeFetchJSON(apiUrl("/api/settings/topology"), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ topology: next ? "lite_mode" : null }), timeout: 5000, }); showToast?.( "Lite Mode " + (next ? "enabled" : "disabled"), next ? "Single-agent path — better for small local models." : "Multi-agent path — uses full CrewAI orchestration." ); } catch (err) { setLiteMode(previous); // rollback setError(err?.message || "Failed to update lite mode"); } finally { setUpdatingLite(false); } }, [liteMode, showToast]); const handlePermissionChange = useCallback(async (next) => { setUpdatingPerm(true); setError(null); const previous = permissionMode; setPermissionMode(next); // optimistic try { const res = await fetch(apiUrl("/api/permissions/mode"), { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ mode: next }), }); if (!res.ok) { const body = await res.json().catch(() => ({})); throw new Error(body.detail || `HTTP ${res.status}`); } showToast?.( "Permission mode updated", `Set to ${next}.` ); } catch (err) { setPermissionMode(previous); // rollback setError(err?.message || "Failed to update permission mode"); } finally { setUpdatingPerm(false); } }, [permissionMode, showToast]); if (loading) { return (
Fine-tune GitPilot's agent behavior and safety settings.
{error && (Use a simplified single-agent prompt instead of the multi-agent CrewAI pipeline. Recommended for small local models (qwen2.5:1.5b, deepseek-r1, phi3:mini) that struggle with the ReAct format.
Controls when the agent needs your approval before writing files or running commands.
Server URL, telemetry, debug logs, environment variables, and more.