diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index ab6b2332fd..c8cbf004cd 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -80,33 +80,102 @@ } }, "settings": { - "advance": { - "maxTurns": { - "description": "Define how many request/response cycles the agent may complete automatically.", - "helper": "Higher values enable longer autonomous runs; lower values keep sessions short.", - "label": "Conversation turn limit" - }, - "permissionMode": { - "description": "Control how the agent handles actions that require approval.", - "label": "Permission mode", - "options": { - "acceptEdits": "Accept edits automatically", - "bypassPermissions": "Bypass permission checks", - "default": "Default (ask before continuing)", - "plan": "Planning mode (requires plan approval)" - }, - "placeholder": "Choose a permission behavior" - }, - "title": "Advanced Settings" + "advance": { + "maxTurns": { + "description": "Define how many request/response cycles the agent may complete automatically.", + "helper": "Higher values enable longer autonomous runs; lower values keep sessions short.", + "label": "Conversation turn limit" + }, + "permissionMode": { + "description": "Control how the agent handles actions that require approval.", + "label": "Permission mode", + "options": { + "acceptEdits": "Accept edits automatically", + "bypassPermissions": "Bypass permission checks", + "default": "Default (ask before continuing)", + "plan": "Planning mode (requires plan approval)" }, - "essential": "Essential Settings", - "mcps": "MCP Servers", - "prompt": "Prompt Settings", - "tools": { - "approved": "approved", - "caution": "Pre-approved tools bypass human review. Enable only trusted tools.", - "description": "Choose which tools can run without manual approval.", - "requiresPermission": "Requires permission when not pre-approved.", + "placeholder": "Choose a permission behavior" + }, + "title": "Advanced Settings" + }, + "essential": "Essential Settings", + "prompt": "Prompt Settings", + "tooling": { + "mcp": { + "description": "Connect MCP servers to unlock additional tools you can approve above.", + "empty": "No MCP servers detected. Add one from the MCP settings page.", + "manageHint": "Need advanced configuration? Visit Settings → MCP Servers.", + "toggle": "Toggle {{name}}" + }, + "permissionMode": { + "acceptEdits": { + "behavior": "Pre-approves trusted filesystem tools so edits run immediately.", + "description": "File edits and filesystem operations are automatically approved.", + "title": "Auto-accept file edits" + }, + "bypassPermissions": { + "behavior": "Every tool is pre-approved automatically.", + "description": "All permission prompts are skipped — use with caution.", + "title": "Bypass permission checks", + "warning": "Use with caution — all tools will run without asking for approval." + }, + "confirmChange": { + "description": "Switching modes updates the automatically approved tools.", + "title": "Change permission mode?" + }, + "default": { + "behavior": "Read-only tools are pre-approved automatically.", + "description": "Read-only tools are pre-approved; everything else still needs permission.", + "title": "Default (ask before continuing)" + }, + "plan": { + "behavior": "Read-only defaults are pre-approved while execution remains disabled.", + "description": "Shares the default read-only tool set but presents a plan before execution.", + "title": "Planning mode (coming soon)" + } + }, + "preapproved": { + "autoBadge": "Added by mode", + "autoDescription": "This tool is auto-approved by the current permission mode.", + "empty": "No tools match your filters.", + "mcpBadge": "MCP tool", + "requiresApproval": "Requires approval when disabled", + "search": "Search tools", + "toggle": "Toggle {{name}}", + "warning": { + "description": "Enable only tools you trust. Mode defaults are highlighted automatically.", + "title": "Pre-approved tools run without manual review." + } + }, + "review": { + "autoTools": "Auto: {{count}}", + "customTools": "Custom: {{count}}", + "helper": "Changes save automatically. Adjust the steps above any time to fine-tune permissions.", + "mcp": "MCP: {{count}}", + "mode": "Mode: {{mode}}" + }, + "steps": { + "mcp": { + "title": "MCP servers" + }, + "permissionMode": { + "title": "Step 1 · Permission mode" + }, + "preapproved": { + "title": "Step 2 · Pre-approved tools" + }, + "review": { + "title": "Step 3 · Review" + } + }, + "tab": "Tooling & permissions" + }, + "tools": { + "approved": "approved", + "caution": "Pre-approved tools bypass human review. Enable only trusted tools.", + "description": "Choose which tools can run without manual approval.", + "requiresPermission": "Requires permission when not pre-approved.", "tab": "Pre-approved tools", "title": "Pre-approved tools", "toggle": "{{defaultValue}}" diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index e798d9000d..bab95f7f8f 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -99,12 +99,81 @@ }, "title": "高级设置" }, - "essential": "基础设置", - "mcps": "MCP 服务器", - "prompt": "提示词设置", - "tools": { - "approved": "已授权", - "caution": "预先授权的工具会跳过人工审核,请仅启用可信的工具。", + "essential": "基础设置", + "prompt": "提示词设置", + "tooling": { + "mcp": { + "description": "连接 MCP 服务器即可解锁更多可在上方预先授权的工具。", + "empty": "未检测到 MCP 服务器,请前往 MCP 设置页添加。", + "manageHint": "需要更多配置?前往 设置 → MCP 服务器。", + "toggle": "切换 {{name}}" + }, + "permissionMode": { + "acceptEdits": { + "behavior": "预先授权受信任的文件系统工具,允许即时执行。", + "description": "文件编辑和文件系统操作将自动通过审批。", + "title": "自动接受文件编辑" + }, + "bypassPermissions": { + "behavior": "所有工具都会被自动预先授权。", + "description": "所有权限提示都会被跳过,请谨慎使用。", + "title": "跳过所有权限检查", + "warning": "危险:所有工具都会在无审批情况下执行。" + }, + "confirmChange": { + "description": "切换模式会更新自动预先授权的工具。", + "title": "确认切换权限模式?" + }, + "default": { + "behavior": "只读工具会自动预先授权。", + "description": "只读工具会自动预先授权,其它操作仍需权限。", + "title": "默认(继续前询问)" + }, + "plan": { + "behavior": "默认的只读工具会自动预先授权,但执行仍被禁用。", + "description": "继承默认的只读工具集,并会在执行前先呈现计划。", + "title": "规划模式(即将支持)" + } + }, + "preapproved": { + "autoBadge": "模式自动添加", + "autoDescription": "该工具由当前权限模式自动预先授权。", + "empty": "没有符合筛选条件的工具。", + "mcpBadge": "MCP 工具", + "requiresApproval": "禁用时需要人工审批", + "search": "搜索工具", + "toggle": "切换 {{name}}", + "warning": { + "description": "仅启用你信任的工具。模式默认值会自动标注。", + "title": "预先授权的工具将在无人工审核时运行。" + } + }, + "review": { + "autoTools": "自动:{{count}}", + "customTools": "自定义:{{count}}", + "helper": "设置会自动保存,可随时返回上方步骤进行调整。", + "mcp": "MCP:{{count}}", + "mode": "模式:{{mode}}" + }, + "steps": { + "mcp": { + "title": "MCP 服务器" + }, + "permissionMode": { + "title": "步骤 1 · 权限模式" + }, + "preapproved": { + "title": "步骤 2 · 预先授权工具" + }, + "review": { + "title": "步骤 3 · 总览" + } + }, + "tab": "工具与权限" + }, + "tools": { + "approved": "已授权", + "caution": "预先授权的工具会跳过人工审核,请仅启用可信的工具。", "description": "选择哪些工具可以在无需人工审批的情况下执行。", "requiresPermission": "未预先授权时需要人工审批。", "tab": "预先授权工具", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 6abfcd0c62..638e3378c5 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -99,12 +99,81 @@ }, "title": "進階設定" }, - "essential": "必要設定", - "mcps": "MCP 伺服器", - "prompt": "提示設定", - "tools": { - "approved": "已授權", - "caution": "預先授權的工具會略過人工審查,請僅啟用可信任的工具。", + "essential": "必要設定", + "prompt": "提示設定", + "tooling": { + "mcp": { + "description": "連線 MCP 伺服器即可解鎖更多可在上方預先授權的工具。", + "empty": "尚未偵測到 MCP 伺服器,請前往 MCP 設定頁新增。", + "manageHint": "需要進階設定?前往 設定 → MCP 伺服器。", + "toggle": "切換 {{name}}" + }, + "permissionMode": { + "acceptEdits": { + "behavior": "預先授權受信任的檔案系統工具,允許即時執行。", + "description": "檔案編輯與檔案系統操作會自動通過核准。", + "title": "自動接受檔案編輯" + }, + "bypassPermissions": { + "behavior": "所有工具都會被自動預先授權。", + "description": "所有權限提示都會被略過,請務必謹慎使用。", + "title": "略過所有權限檢查", + "warning": "警告:所有工具都會在無核准情況下執行。" + }, + "confirmChange": { + "description": "切換模式會更新自動預先授權的工具。", + "title": "確認切換權限模式?" + }, + "default": { + "behavior": "唯讀工具會自動預先授權。", + "description": "唯讀工具會自動預先授權,其它操作仍需核准。", + "title": "預設(繼續前先詢問)" + }, + "plan": { + "behavior": "預設的唯讀工具會自動預先授權,但執行仍被停用。", + "description": "沿用預設的唯讀工具集,並會在執行前先呈現計畫。", + "title": "規劃模式(即將支援)" + } + }, + "preapproved": { + "autoBadge": "模式自動添加", + "autoDescription": "此工具由目前的權限模式自動預先授權。", + "empty": "沒有符合篩選條件的工具。", + "mcpBadge": "MCP 工具", + "requiresApproval": "停用時需要人工核准", + "search": "搜尋工具", + "toggle": "切換 {{name}}", + "warning": { + "description": "僅啟用你信任的工具。模式預設值會自動標示。", + "title": "預先授權的工具將在無人工審查下執行。" + } + }, + "review": { + "autoTools": "自動:{{count}}", + "customTools": "自訂:{{count}}", + "helper": "設定會自動儲存,可隨時回到上方步驟調整。", + "mcp": "MCP:{{count}}", + "mode": "模式:{{mode}}" + }, + "steps": { + "mcp": { + "title": "MCP 伺服器" + }, + "permissionMode": { + "title": "步驟 1 · 權限模式" + }, + "preapproved": { + "title": "步驟 2 · 預先授權工具" + }, + "review": { + "title": "步驟 3 · 檢視" + } + }, + "tab": "工具與權限" + }, + "tools": { + "approved": "已授權", + "caution": "預先授權的工具會略過人工審查,請僅啟用可信任的工具。", "description": "選擇哪些工具可在無需人工核准的情況下執行。", "requiresPermission": "未預先授權時需要人工核准。", "tab": "預先授權工具", diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 982956f49c..25f7b81519 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -99,12 +99,81 @@ }, "title": "[to be translated]:高级设置" }, - "essential": "Βασικές Ρυθμίσεις", - "mcps": "[to be translated]:MCP 服务器", - "prompt": "Ρυθμίσεις Προτροπής", - "tools": { - "approved": "[to be translated]:approved", - "caution": "[to be translated]:Pre-approved tools bypass human review. Enable only trusted tools.", + "essential": "Βασικές Ρυθμίσεις", + "prompt": "Ρυθμίσεις Προτροπής", + "tooling": { + "mcp": { + "description": "[to be translated]:Connect MCP servers to unlock additional tools you can approve above.", + "empty": "[to be translated]:No MCP servers detected. Add one from the MCP settings page.", + "manageHint": "[to be translated]:Need advanced configuration? Visit Settings → MCP Servers.", + "toggle": "[to be translated]:Toggle {{name}}" + }, + "permissionMode": { + "acceptEdits": { + "behavior": "[to be translated]:Pre-approves trusted filesystem tools so edits run immediately.", + "description": "[to be translated]:File edits and filesystem operations are automatically approved.", + "title": "[to be translated]:Auto-accept file edits" + }, + "bypassPermissions": { + "behavior": "[to be translated]:Every tool is pre-approved automatically.", + "description": "[to be translated]:All permission prompts are skipped — use with caution.", + "title": "[to be translated]:Bypass permission checks", + "warning": "[to be translated]:Use with caution — all tools will run without asking for approval." + }, + "confirmChange": { + "description": "[to be translated]:Switching modes updates the automatically approved tools.", + "title": "[to be translated]:Change permission mode?" + }, + "default": { + "behavior": "[to be translated]:No tools are pre-approved automatically.", + "description": "[to be translated]:Normal permission checks apply.", + "title": "[to be translated]:Default (ask before continuing)" + }, + "plan": { + "behavior": "[to be translated]:Read-only tools only. Execution is disabled.", + "description": "[to be translated]:Claude can only use read-only tools and presents a plan before execution.", + "title": "[to be translated]:Planning mode (coming soon)" + } + }, + "preapproved": { + "autoBadge": "[to be translated]:Added by mode", + "autoDescription": "[to be translated]:This tool is auto-approved by the current permission mode.", + "empty": "[to be translated]:No tools match your filters.", + "mcpBadge": "[to be translated]:MCP tool", + "requiresApproval": "[to be translated]:Requires approval when disabled", + "search": "[to be translated]:Search tools", + "toggle": "[to be translated]:Toggle {{name}}", + "warning": { + "description": "[to be translated]:Enable only tools you trust. Mode defaults are highlighted automatically.", + "title": "[to be translated]:Pre-approved tools run without manual review." + } + }, + "review": { + "autoTools": "[to be translated]:Auto: {{count}}", + "customTools": "[to be translated]:Custom: {{count}}", + "helper": "[to be translated]:Changes save automatically. Adjust the steps above any time to fine-tune permissions.", + "mcp": "[to be translated]:MCP: {{count}}", + "mode": "[to be translated]:Mode: {{mode}}" + }, + "steps": { + "mcp": { + "title": "[to be translated]:MCP servers" + }, + "permissionMode": { + "title": "[to be translated]:Step 1 · Permission mode" + }, + "preapproved": { + "title": "[to be translated]:Step 2 · Pre-approved tools" + }, + "review": { + "title": "[to be translated]:Step 3 · Review" + } + }, + "tab": "[to be translated]:Tooling & permissions" + }, + "tools": { + "approved": "[to be translated]:approved", + "caution": "[to be translated]:Pre-approved tools bypass human review. Enable only trusted tools.", "description": "[to be translated]:Choose which tools can run without manual approval.", "requiresPermission": "[to be translated]:Requires permission when not pre-approved.", "tab": "[to be translated]:Pre-approved tools", diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index f0f1ec3b5e..728f9e3a0f 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -99,12 +99,81 @@ }, "title": "[to be translated]:高级设置" }, - "essential": "Configuraciones esenciales", - "mcps": "[to be translated]:MCP 服务器", - "prompt": "Configuración de indicaciones", - "tools": { - "approved": "[to be translated]:approved", - "caution": "[to be translated]:Pre-approved tools bypass human review. Enable only trusted tools.", + "essential": "Configuraciones esenciales", + "prompt": "Configuración de indicaciones", + "tooling": { + "mcp": { + "description": "[to be translated]:Connect MCP servers to unlock additional tools you can approve above.", + "empty": "[to be translated]:No MCP servers detected. Add one from the MCP settings page.", + "manageHint": "[to be translated]:Need advanced configuration? Visit Settings → MCP Servers.", + "toggle": "[to be translated]:Toggle {{name}}" + }, + "permissionMode": { + "acceptEdits": { + "behavior": "[to be translated]:Pre-approves trusted filesystem tools so edits run immediately.", + "description": "[to be translated]:File edits and filesystem operations are automatically approved.", + "title": "[to be translated]:Auto-accept file edits" + }, + "bypassPermissions": { + "behavior": "[to be translated]:Every tool is pre-approved automatically.", + "description": "[to be translated]:All permission prompts are skipped — use with caution.", + "title": "[to be translated]:Bypass permission checks", + "warning": "[to be translated]:Use with caution — all tools will run without asking for approval." + }, + "confirmChange": { + "description": "[to be translated]:Switching modes updates the automatically approved tools.", + "title": "[to be translated]:Change permission mode?" + }, + "default": { + "behavior": "[to be translated]:No tools are pre-approved automatically.", + "description": "[to be translated]:Normal permission checks apply.", + "title": "[to be translated]:Default (ask before continuing)" + }, + "plan": { + "behavior": "[to be translated]:Read-only tools only. Execution is disabled.", + "description": "[to be translated]:Claude can only use read-only tools and presents a plan before execution.", + "title": "[to be translated]:Planning mode (coming soon)" + } + }, + "preapproved": { + "autoBadge": "[to be translated]:Added by mode", + "autoDescription": "[to be translated]:This tool is auto-approved by the current permission mode.", + "empty": "[to be translated]:No tools match your filters.", + "mcpBadge": "[to be translated]:MCP tool", + "requiresApproval": "[to be translated]:Requires approval when disabled", + "search": "[to be translated]:Search tools", + "toggle": "[to be translated]:Toggle {{name}}", + "warning": { + "description": "[to be translated]:Enable only tools you trust. Mode defaults are highlighted automatically.", + "title": "[to be translated]:Pre-approved tools run without manual review." + } + }, + "review": { + "autoTools": "[to be translated]:Auto: {{count}}", + "customTools": "[to be translated]:Custom: {{count}}", + "helper": "[to be translated]:Changes save automatically. Adjust the steps above any time to fine-tune permissions.", + "mcp": "[to be translated]:MCP: {{count}}", + "mode": "[to be translated]:Mode: {{mode}}" + }, + "steps": { + "mcp": { + "title": "[to be translated]:MCP servers" + }, + "permissionMode": { + "title": "[to be translated]:Step 1 · Permission mode" + }, + "preapproved": { + "title": "[to be translated]:Step 2 · Pre-approved tools" + }, + "review": { + "title": "[to be translated]:Step 3 · Review" + } + }, + "tab": "[to be translated]:Tooling & permissions" + }, + "tools": { + "approved": "[to be translated]:approved", + "caution": "[to be translated]:Pre-approved tools bypass human review. Enable only trusted tools.", "description": "[to be translated]:Choose which tools can run without manual approval.", "requiresPermission": "[to be translated]:Requires permission when not pre-approved.", "tab": "[to be translated]:Pre-approved tools", diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index 7fa0ed81ca..9159c68e63 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -99,12 +99,81 @@ }, "title": "[to be translated]:高级设置" }, - "essential": "Paramètres essentiels", - "mcps": "[to be translated]:MCP 服务器", - "prompt": "Paramètres de l'invite", - "tools": { - "approved": "[to be translated]:approved", - "caution": "[to be translated]:Pre-approved tools bypass human review. Enable only trusted tools.", + "essential": "Paramètres essentiels", + "prompt": "Paramètres de l'invite", + "tooling": { + "mcp": { + "description": "[to be translated]:Connect MCP servers to unlock additional tools you can approve above.", + "empty": "[to be translated]:No MCP servers detected. Add one from the MCP settings page.", + "manageHint": "[to be translated]:Need advanced configuration? Visit Settings → MCP Servers.", + "toggle": "[to be translated]:Toggle {{name}}" + }, + "permissionMode": { + "acceptEdits": { + "behavior": "[to be translated]:Pre-approves trusted filesystem tools so edits run immediately.", + "description": "[to be translated]:File edits and filesystem operations are automatically approved.", + "title": "[to be translated]:Auto-accept file edits" + }, + "bypassPermissions": { + "behavior": "[to be translated]:Every tool is pre-approved automatically.", + "description": "[to be translated]:All permission prompts are skipped — use with caution.", + "title": "[to be translated]:Bypass permission checks", + "warning": "[to be translated]:Use with caution — all tools will run without asking for approval." + }, + "confirmChange": { + "description": "[to be translated]:Switching modes updates the automatically approved tools.", + "title": "[to be translated]:Change permission mode?" + }, + "default": { + "behavior": "[to be translated]:No tools are pre-approved automatically.", + "description": "[to be translated]:Normal permission checks apply.", + "title": "[to be translated]:Default (ask before continuing)" + }, + "plan": { + "behavior": "[to be translated]:Read-only tools only. Execution is disabled.", + "description": "[to be translated]:Claude can only use read-only tools and presents a plan before execution.", + "title": "[to be translated]:Planning mode (coming soon)" + } + }, + "preapproved": { + "autoBadge": "[to be translated]:Added by mode", + "autoDescription": "[to be translated]:This tool is auto-approved by the current permission mode.", + "empty": "[to be translated]:No tools match your filters.", + "mcpBadge": "[to be translated]:MCP tool", + "requiresApproval": "[to be translated]:Requires approval when disabled", + "search": "[to be translated]:Search tools", + "toggle": "[to be translated]:Toggle {{name}}", + "warning": { + "description": "[to be translated]:Enable only tools you trust. Mode defaults are highlighted automatically.", + "title": "[to be translated]:Pre-approved tools run without manual review." + } + }, + "review": { + "autoTools": "[to be translated]:Auto: {{count}}", + "customTools": "[to be translated]:Custom: {{count}}", + "helper": "[to be translated]:Changes save automatically. Adjust the steps above any time to fine-tune permissions.", + "mcp": "[to be translated]:MCP: {{count}}", + "mode": "[to be translated]:Mode: {{mode}}" + }, + "steps": { + "mcp": { + "title": "[to be translated]:MCP servers" + }, + "permissionMode": { + "title": "[to be translated]:Step 1 · Permission mode" + }, + "preapproved": { + "title": "[to be translated]:Step 2 · Pre-approved tools" + }, + "review": { + "title": "[to be translated]:Step 3 · Review" + } + }, + "tab": "[to be translated]:Tooling & permissions" + }, + "tools": { + "approved": "[to be translated]:approved", + "caution": "[to be translated]:Pre-approved tools bypass human review. Enable only trusted tools.", "description": "[to be translated]:Choose which tools can run without manual approval.", "requiresPermission": "[to be translated]:Requires permission when not pre-approved.", "tab": "[to be translated]:Pre-approved tools", diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index 375326d047..78f3cac1aa 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -99,12 +99,81 @@ }, "title": "[to be translated]:高级设置" }, - "essential": "必須設定", - "mcps": "[to be translated]:MCP 服务器", - "prompt": "プロンプト設定", - "tools": { - "approved": "承認済み", - "caution": "事前承認したツールは人によるレビューをスキップします。信頼できるツールのみ有効にしてください。", + "essential": "必須設定", + "prompt": "プロンプト設定", + "tooling": { + "mcp": { + "description": "[to be translated]:Connect MCP servers to unlock additional tools you can approve above.", + "empty": "[to be translated]:No MCP servers detected. Add one from the MCP settings page.", + "manageHint": "[to be translated]:Need advanced configuration? Visit Settings → MCP Servers.", + "toggle": "[to be translated]:Toggle {{name}}" + }, + "permissionMode": { + "acceptEdits": { + "behavior": "[to be translated]:Pre-approves trusted filesystem tools so edits run immediately.", + "description": "[to be translated]:File edits and filesystem operations are automatically approved.", + "title": "[to be translated]:Auto-accept file edits" + }, + "bypassPermissions": { + "behavior": "[to be translated]:Every tool is pre-approved automatically.", + "description": "[to be translated]:All permission prompts are skipped — use with caution.", + "title": "[to be translated]:Bypass permission checks", + "warning": "[to be translated]:Use with caution — all tools will run without asking for approval." + }, + "confirmChange": { + "description": "[to be translated]:Switching modes updates the automatically approved tools.", + "title": "[to be translated]:Change permission mode?" + }, + "default": { + "behavior": "[to be translated]:No tools are pre-approved automatically.", + "description": "[to be translated]:Normal permission checks apply.", + "title": "[to be translated]:Default (ask before continuing)" + }, + "plan": { + "behavior": "[to be translated]:Read-only tools only. Execution is disabled.", + "description": "[to be translated]:Claude can only use read-only tools and presents a plan before execution.", + "title": "[to be translated]:Planning mode (coming soon)" + } + }, + "preapproved": { + "autoBadge": "[to be translated]:Added by mode", + "autoDescription": "[to be translated]:This tool is auto-approved by the current permission mode.", + "empty": "[to be translated]:No tools match your filters.", + "mcpBadge": "[to be translated]:MCP tool", + "requiresApproval": "[to be translated]:Requires approval when disabled", + "search": "[to be translated]:Search tools", + "toggle": "[to be translated]:Toggle {{name}}", + "warning": { + "description": "[to be translated]:Enable only tools you trust. Mode defaults are highlighted automatically.", + "title": "[to be translated]:Pre-approved tools run without manual review." + } + }, + "review": { + "autoTools": "[to be translated]:Auto: {{count}}", + "customTools": "[to be translated]:Custom: {{count}}", + "helper": "[to be translated]:Changes save automatically. Adjust the steps above any time to fine-tune permissions.", + "mcp": "[to be translated]:MCP: {{count}}", + "mode": "[to be translated]:Mode: {{mode}}" + }, + "steps": { + "mcp": { + "title": "[to be translated]:MCP servers" + }, + "permissionMode": { + "title": "[to be translated]:Step 1 · Permission mode" + }, + "preapproved": { + "title": "[to be translated]:Step 2 · Pre-approved tools" + }, + "review": { + "title": "[to be translated]:Step 3 · Review" + } + }, + "tab": "[to be translated]:Tooling & permissions" + }, + "tools": { + "approved": "承認済み", + "caution": "事前承認したツールは人によるレビューをスキップします。信頼できるツールのみ有効にしてください。", "description": "人による承認なしで実行できるツールを選択します。", "requiresPermission": "事前承認されていない場合は承認が必要です。", "tab": "事前承認済みツール", diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index 57fd837c40..1c0fce2198 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -99,12 +99,81 @@ }, "title": "[to be translated]:高级设置" }, - "essential": "Configurações Essenciais", - "mcps": "[to be translated]:MCP 服务器", - "prompt": "Configurações de Prompt", - "tools": { - "approved": "[to be translated]:approved", - "caution": "[to be translated]:Pre-approved tools bypass human review. Enable only trusted tools.", + "essential": "Configurações Essenciais", + "prompt": "Configurações de Prompt", + "tooling": { + "mcp": { + "description": "[to be translated]:Connect MCP servers to unlock additional tools you can approve above.", + "empty": "[to be translated]:No MCP servers detected. Add one from the MCP settings page.", + "manageHint": "[to be translated]:Need advanced configuration? Visit Settings → MCP Servers.", + "toggle": "[to be translated]:Toggle {{name}}" + }, + "permissionMode": { + "acceptEdits": { + "behavior": "[to be translated]:Pre-approves trusted filesystem tools so edits run immediately.", + "description": "[to be translated]:File edits and filesystem operations are automatically approved.", + "title": "[to be translated]:Auto-accept file edits" + }, + "bypassPermissions": { + "behavior": "[to be translated]:Every tool is pre-approved automatically.", + "description": "[to be translated]:All permission prompts are skipped — use with caution.", + "title": "[to be translated]:Bypass permission checks", + "warning": "[to be translated]:Use with caution — all tools will run without asking for approval." + }, + "confirmChange": { + "description": "[to be translated]:Switching modes updates the automatically approved tools.", + "title": "[to be translated]:Change permission mode?" + }, + "default": { + "behavior": "[to be translated]:No tools are pre-approved automatically.", + "description": "[to be translated]:Normal permission checks apply.", + "title": "[to be translated]:Default (ask before continuing)" + }, + "plan": { + "behavior": "[to be translated]:Read-only tools only. Execution is disabled.", + "description": "[to be translated]:Claude can only use read-only tools and presents a plan before execution.", + "title": "[to be translated]:Planning mode (coming soon)" + } + }, + "preapproved": { + "autoBadge": "[to be translated]:Added by mode", + "autoDescription": "[to be translated]:This tool is auto-approved by the current permission mode.", + "empty": "[to be translated]:No tools match your filters.", + "mcpBadge": "[to be translated]:MCP tool", + "requiresApproval": "[to be translated]:Requires approval when disabled", + "search": "[to be translated]:Search tools", + "toggle": "[to be translated]:Toggle {{name}}", + "warning": { + "description": "[to be translated]:Enable only tools you trust. Mode defaults are highlighted automatically.", + "title": "[to be translated]:Pre-approved tools run without manual review." + } + }, + "review": { + "autoTools": "[to be translated]:Auto: {{count}}", + "customTools": "[to be translated]:Custom: {{count}}", + "helper": "[to be translated]:Changes save automatically. Adjust the steps above any time to fine-tune permissions.", + "mcp": "[to be translated]:MCP: {{count}}", + "mode": "[to be translated]:Mode: {{mode}}" + }, + "steps": { + "mcp": { + "title": "[to be translated]:MCP servers" + }, + "permissionMode": { + "title": "[to be translated]:Step 1 · Permission mode" + }, + "preapproved": { + "title": "[to be translated]:Step 2 · Pre-approved tools" + }, + "review": { + "title": "[to be translated]:Step 3 · Review" + } + }, + "tab": "[to be translated]:Tooling & permissions" + }, + "tools": { + "approved": "[to be translated]:approved", + "caution": "[to be translated]:Pre-approved tools bypass human review. Enable only trusted tools.", "description": "[to be translated]:Choose which tools can run without manual approval.", "requiresPermission": "[to be translated]:Requires permission when not pre-approved.", "tab": "[to be translated]:Pre-approved tools", diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index 175c54b585..baa8b5f67c 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -99,12 +99,81 @@ }, "title": "[to be translated]:高级设置" }, - "essential": "Основные настройки", - "mcps": "[to be translated]:MCP 服务器", - "prompt": "Настройки подсказки", - "tools": { - "approved": "[to be translated]:approved", - "caution": "[to be translated]:Pre-approved tools bypass human review. Enable only trusted tools.", + "essential": "Основные настройки", + "prompt": "Настройки подсказки", + "tooling": { + "mcp": { + "description": "[to be translated]:Connect MCP servers to unlock additional tools you can approve above.", + "empty": "[to be translated]:No MCP servers detected. Add one from the MCP settings page.", + "manageHint": "[to be translated]:Need advanced configuration? Visit Settings → MCP Servers.", + "toggle": "[to be translated]:Toggle {{name}}" + }, + "permissionMode": { + "acceptEdits": { + "behavior": "[to be translated]:Pre-approves trusted filesystem tools so edits run immediately.", + "description": "[to be translated]:File edits and filesystem operations are automatically approved.", + "title": "[to be translated]:Auto-accept file edits" + }, + "bypassPermissions": { + "behavior": "[to be translated]:Every tool is pre-approved automatically.", + "description": "[to be translated]:All permission prompts are skipped — use with caution.", + "title": "[to be translated]:Bypass permission checks", + "warning": "[to be translated]:Use with caution — all tools will run without asking for approval." + }, + "confirmChange": { + "description": "[to be translated]:Switching modes updates the automatically approved tools.", + "title": "[to be translated]:Change permission mode?" + }, + "default": { + "behavior": "[to be translated]:No tools are pre-approved automatically.", + "description": "[to be translated]:Normal permission checks apply.", + "title": "[to be translated]:Default (ask before continuing)" + }, + "plan": { + "behavior": "[to be translated]:Read-only tools only. Execution is disabled.", + "description": "[to be translated]:Claude can only use read-only tools and presents a plan before execution.", + "title": "[to be translated]:Planning mode (coming soon)" + } + }, + "preapproved": { + "autoBadge": "[to be translated]:Added by mode", + "autoDescription": "[to be translated]:This tool is auto-approved by the current permission mode.", + "empty": "[to be translated]:No tools match your filters.", + "mcpBadge": "[to be translated]:MCP tool", + "requiresApproval": "[to be translated]:Requires approval when disabled", + "search": "[to be translated]:Search tools", + "toggle": "[to be translated]:Toggle {{name}}", + "warning": { + "description": "[to be translated]:Enable only tools you trust. Mode defaults are highlighted automatically.", + "title": "[to be translated]:Pre-approved tools run without manual review." + } + }, + "review": { + "autoTools": "[to be translated]:Auto: {{count}}", + "customTools": "[to be translated]:Custom: {{count}}", + "helper": "[to be translated]:Changes save automatically. Adjust the steps above any time to fine-tune permissions.", + "mcp": "[to be translated]:MCP: {{count}}", + "mode": "[to be translated]:Mode: {{mode}}" + }, + "steps": { + "mcp": { + "title": "[to be translated]:MCP servers" + }, + "permissionMode": { + "title": "[to be translated]:Step 1 · Permission mode" + }, + "preapproved": { + "title": "[to be translated]:Step 2 · Pre-approved tools" + }, + "review": { + "title": "[to be translated]:Step 3 · Review" + } + }, + "tab": "[to be translated]:Tooling & permissions" + }, + "tools": { + "approved": "[to be translated]:approved", + "caution": "[to be translated]:Pre-approved tools bypass human review. Enable only trusted tools.", "description": "[to be translated]:Choose which tools can run without manual approval.", "requiresPermission": "[to be translated]:Requires permission when not pre-approved.", "tab": "[to be translated]:Pre-approved tools", diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentAdvanceSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentAdvanceSettings.tsx index 0e0c4a5583..9fb4395e61 100644 --- a/src/renderer/src/pages/settings/AgentSettings/AgentAdvanceSettings.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/AgentAdvanceSettings.tsx @@ -1,33 +1,16 @@ -import { Input, Select, SelectItem, Tooltip } from '@heroui/react' -import type { Selection } from '@react-types/shared' +import { Input, Tooltip } from '@heroui/react' import { AgentConfiguration, AgentConfigurationSchema, GetAgentResponse, - PermissionMode, - PermissionModeSchema, UpdateAgentForm } from '@renderer/types' import { Info } from 'lucide-react' -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { SettingsContainer, SettingsItem, SettingsTitle } from './shared' -const permissionModeKeyMap: Record = { - default: 'agent.settings.advance.permissionMode.options.default', - acceptEdits: 'agent.settings.advance.permissionMode.options.acceptEdits', - bypassPermissions: 'agent.settings.advance.permissionMode.options.bypassPermissions', - plan: 'agent.settings.advance.permissionMode.options.plan' -} - -const permissionModeFallback: Record = { - default: 'Default (ask before continuing)', - acceptEdits: 'Accept edits automatically', - bypassPermissions: 'Bypass permission checks', - plan: 'Planning mode (requires plan approval)' -} - type AgentConfigurationState = AgentConfiguration & Record interface AgentAdvanceSettingsProps { @@ -53,33 +36,6 @@ export const AgentAdvanceSettings: React.FC = ({ agen setMaxTurnsInput(String(parsed.max_turns)) }, [agent]) - const permissionOptions = useMemo( - () => - PermissionModeSchema.options.map((mode) => ({ - key: mode, - label: t(permissionModeKeyMap[mode], permissionModeFallback[mode]) - })) satisfies { key: PermissionMode; label: string }[], - [t] - ) - - const handlePermissionChange = useCallback( - (keys: Selection) => { - if (!agent || keys === 'all') return - const [first] = Array.from(keys) - if (!first) return - const nextMode = first as PermissionMode - setConfiguration((prev) => { - if (prev.permission_mode === nextMode) { - return prev - } - const next = { ...prev, permission_mode: nextMode } as AgentConfigurationState - updateAgent({ id: agent.id, configuration: next } satisfies UpdateAgentForm) - return next - }) - }, - [agent, updateAgent] - ) - const commitMaxTurns = useCallback(() => { if (!agent) return const parsedValue = Number.parseInt(maxTurnsInput, 10) @@ -104,29 +60,6 @@ export const AgentAdvanceSettings: React.FC = ({ agen return ( - - - - - }> - {t('agent.settings.advance.permissionMode.label')} - - - Promise | void -} - -export const AgentMCPSettings: React.FC = ({ agent, updateAgent }) => { - const { t } = useTranslation() - const { mcpServers: allMcpServers } = useMCPServers() - const [selectedIds, setSelectedIds] = useState([]) - - const availableServers = useMemo(() => allMcpServers ?? [], [allMcpServers]) - - useEffect(() => { - if (!agent) { - setSelectedIds([]) - return - } - const mcps = agent.mcps ?? [] - const validIds = mcps.filter((id) => availableServers.some((server) => server.id === id)) - setSelectedIds((prev) => { - if (prev.length === validIds.length && prev.every((id) => validIds.includes(id))) { - return prev - } - return validIds - }) - }, [agent, availableServers]) - - const handleToggle = useCallback( - (serverId: string, isEnabled: boolean) => { - if (!agent) return - - setSelectedIds((prev) => { - const exists = prev.includes(serverId) - if (isEnabled === exists) { - return prev - } - const next = isEnabled ? [...prev, serverId] : prev.filter((id) => id !== serverId) - updateAgent({ id: agent.id, mcps: next }) - return next - }) - }, - [agent, updateAgent] - ) - - const enabledCount = useMemo(() => { - const validSelected = selectedIds.filter((id) => availableServers.some((server) => server.id === id)) - return validSelected.length - }, [selectedIds, availableServers]) - - const renderServerMeta = useCallback((meta?: ReactNode) => { - if (!meta) return null - return {meta} - }, []) - - if (!agent) { - return null - } - - return ( - - -
-
- - {t('assistants.settings.mcp.title')} - - - - - {availableServers.length > 0 ? ( - - {enabledCount} / {availableServers.length} {t('settings.mcp.active')} - - ) : null} -
- - {availableServers.length > 0 ? ( -
- {availableServers.map((server) => { - const isSelected = selectedIds.includes(server.id) - return ( - - -
- {server.name} - {server.description ? ( - {server.description} - ) : null} -
- handleToggle(server.id, value)} - /> -
- - {renderServerMeta(server.baseUrl)} - {renderServerMeta(server.provider)} - -
- ) - })} -
- ) : ( -
- {t('assistants.settings.mcp.noServersAvailable', 'No MCP servers available')} -
- )} -
-
-
- ) -} - -export default AgentMCPSettings diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentToolSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentToolSettings.tsx deleted file mode 100644 index fdd4f0cce0..0000000000 --- a/src/renderer/src/pages/settings/AgentSettings/AgentToolSettings.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { Alert, Card, CardBody, CardHeader, Switch, Tooltip } from '@heroui/react' -import { GetAgentResponse, Tool, UpdateAgentForm } from '@renderer/types' -import { Info } from 'lucide-react' -import { FC, useCallback, useEffect, useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' - -import { SettingsContainer, SettingsItem, SettingsTitle } from './shared' - -interface AgentToolSettingsProps { - agent: GetAgentResponse | undefined | null - updateAgent: (form: UpdateAgentForm) => Promise | void -} - -const isSameSelection = (next: string[], previous: string[]) => { - if (next.length !== previous.length) { - return false - } - const previousSet = new Set(previous) - return next.every((id) => previousSet.has(id)) -} - -export const AgentToolSettings: FC = ({ agent, updateAgent }) => { - const { t } = useTranslation() - const [approvedIds, setApprovedIds] = useState([]) - - const availableTools = useMemo(() => agent?.tools ?? [], [agent?.tools]) - - useEffect(() => { - if (!agent) { - setApprovedIds((prev) => (prev.length === 0 ? prev : [])) - return - } - const allowed = agent.allowed_tools ?? [] - const validIds = allowed.filter((id) => availableTools.some((tool) => tool.id === id)) - setApprovedIds((prev) => { - if (isSameSelection(prev, validIds)) { - return prev - } - return validIds - }) - }, [agent, availableTools]) - - const handleToggle = useCallback( - (toolId: string, isApproved: boolean) => { - if (!agent) return - - setApprovedIds((prev) => { - const exists = prev.includes(toolId) - if (isApproved === exists) { - return prev - } - const next = isApproved ? [...prev, toolId] : prev.filter((id) => id !== toolId) - const previous = agent.allowed_tools ?? [] - if (!isSameSelection(next, previous)) { - updateAgent({ id: agent.id, allowed_tools: next }) - } - return next - }) - }, - [agent, updateAgent] - ) - - const approvedCount = useMemo(() => { - return approvedIds.filter((id) => availableTools.some((tool) => tool.id === id)).length - }, [approvedIds, availableTools]) - - if (!agent) { - return null - } - - return ( - - -
-
- - {t('agent.settings.tools.title', 'Pre-approved tools')} - - - - - {availableTools.length > 0 ? ( - - {approvedCount} / {availableTools.length} {t('agent.settings.tools.approved', 'approved')} - - ) : null} -
- - - - {availableTools.length > 0 ? ( -
- {availableTools.map((tool) => { - const isApproved = approvedIds.includes(tool.id) - return ( - - -
- {tool.name} - {tool.description ? ( - {tool.description} - ) : null} -
- handleToggle(tool.id, value)} - /> -
- {tool.requirePermissions ? ( - - - {t( - 'agent.settings.tools.requiresPermission', - 'Requires permission when not pre-approved.' - )} - - - ) : null} -
- ) - })} -
- ) : ( -
- {t('agent.session.allowed_tools.empty')} -
- )} -
-
-
- ) -} - -export default AgentToolSettings diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentToolingSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentToolingSettings.tsx new file mode 100644 index 0000000000..332eeee1bd --- /dev/null +++ b/src/renderer/src/pages/settings/AgentSettings/AgentToolingSettings.tsx @@ -0,0 +1,541 @@ +import { Alert, Card, CardBody, CardHeader, Chip, Input, Switch } from '@heroui/react' +import { useAgentClient } from '@renderer/hooks/agents/useAgentClient' +import { useMCPServers } from '@renderer/hooks/useMCPServers' +import { + AgentConfiguration, + AgentConfigurationSchema, + GetAgentResponse, + PermissionMode, + Tool, + UpdateAgentForm +} from '@renderer/types' +import { Modal } from 'antd' +import { ShieldAlert, ShieldCheck, Wrench } from 'lucide-react' +import { FC, useCallback, useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { mutate } from 'swr' + +import { SettingsContainer, SettingsItem, SettingsTitle } from './shared' + +interface AgentToolingSettingsProps { + agent: GetAgentResponse | undefined | null + updateAgent: (form: UpdateAgentForm) => Promise | void +} + +type AgentConfigurationState = AgentConfiguration & Record + +type PermissionModeCard = { + mode: PermissionMode + titleKey: string + titleFallback: string + descriptionKey: string + descriptionFallback: string + behaviorKey: string + behaviorFallback: string + caution?: boolean + unsupported?: boolean +} + +const defaultConfiguration = AgentConfigurationSchema.parse({}) as AgentConfigurationState + +const permissionModeCards: PermissionModeCard[] = [ + { + mode: 'default', + titleKey: 'agent.settings.tooling.permissionMode.default.title', + titleFallback: 'Default (ask before continuing)', + descriptionKey: 'agent.settings.tooling.permissionMode.default.description', + descriptionFallback: 'Read-only tools are pre-approved; everything else still needs permission.', + behaviorKey: 'agent.settings.tooling.permissionMode.default.behavior', + behaviorFallback: 'Read-only tools are pre-approved automatically.' + }, + { + mode: 'plan', + titleKey: 'agent.settings.tooling.permissionMode.plan.title', + titleFallback: 'Planning mode', + descriptionKey: 'agent.settings.tooling.permissionMode.plan.description', + descriptionFallback: 'Shares the default read-only tool set but presents a plan before execution.', + behaviorKey: 'agent.settings.tooling.permissionMode.plan.behavior', + behaviorFallback: 'Read-only defaults are pre-approved while execution remains disabled.' + }, + { + mode: 'acceptEdits', + titleKey: 'agent.settings.tooling.permissionMode.acceptEdits.title', + titleFallback: 'Auto-accept file edits', + descriptionKey: 'agent.settings.tooling.permissionMode.acceptEdits.description', + descriptionFallback: 'File edits and filesystem operations are automatically approved.', + behaviorKey: 'agent.settings.tooling.permissionMode.acceptEdits.behavior', + behaviorFallback: 'Pre-approves trusted filesystem tools so edits run immediately.' + }, + { + mode: 'bypassPermissions', + titleKey: 'agent.settings.tooling.permissionMode.bypassPermissions.title', + titleFallback: 'Bypass permission checks', + descriptionKey: 'agent.settings.tooling.permissionMode.bypassPermissions.description', + descriptionFallback: 'All permission prompts are skipped — use with caution.', + behaviorKey: 'agent.settings.tooling.permissionMode.bypassPermissions.behavior', + behaviorFallback: 'Every tool is pre-approved automatically.', + caution: true + } +] + +const computeModeDefaults = (mode: PermissionMode, tools: Tool[]): string[] => { + const defaultToolIds = tools.filter((tool) => !tool.requirePermissions).map((tool) => tool.id) + switch (mode) { + case 'acceptEdits': + return [ + ...defaultToolIds, + 'Edit', + 'MultiEdit', + 'NotebookEdit', + 'Write', + 'Bash(mkdir:*)', + 'Bash(touch:*)', + 'Bash(rm:*)', + 'Bash(mv:*)', + 'Bash(cp:*)' + ] + case 'bypassPermissions': + return tools.map((tool) => tool.id) + case 'default': + case 'plan': + return defaultToolIds + } +} + +const unique = (values: string[]) => Array.from(new Set(values)) + +export const AgentToolingSettings: FC = ({ agent, updateAgent }) => { + const { t } = useTranslation() + const client = useAgentClient() + const { mcpServers: allServers } = useMCPServers() + + const [configuration, setConfiguration] = useState(defaultConfiguration) + const [selectedMode, setSelectedMode] = useState(defaultConfiguration.permission_mode) + const [autoToolIds, setAutoToolIds] = useState([]) + const [approvedToolIds, setApprovedToolIds] = useState([]) + const [searchTerm, setSearchTerm] = useState('') + const [isUpdatingMode, setIsUpdatingMode] = useState(false) + const [isUpdatingTools, setIsUpdatingTools] = useState(false) + const [selectedMcpIds, setSelectedMcpIds] = useState([]) + const [isUpdatingMcp, setIsUpdatingMcp] = useState(false) + + const availableTools = useMemo(() => agent?.tools ?? [], [agent?.tools]) + const availableServers = useMemo(() => allServers ?? [], [allServers]) + + useEffect(() => { + if (!agent) { + setConfiguration(defaultConfiguration) + setSelectedMode(defaultConfiguration.permission_mode) + setApprovedToolIds([]) + setAutoToolIds([]) + setSelectedMcpIds([]) + return + } + const parsed = AgentConfigurationSchema.parse(agent.configuration ?? {}) as AgentConfigurationState + setConfiguration(parsed) + setSelectedMode(parsed.permission_mode) + + const defaults = computeModeDefaults(parsed.permission_mode, availableTools) + setAutoToolIds(defaults) + const allowed = agent.allowed_tools ?? [] + setApprovedToolIds((prev) => { + const sanitized = allowed.filter((id) => availableTools.some((tool) => tool.id === id)) + const isSame = sanitized.length === prev.length && sanitized.every((id) => prev.includes(id)) + if (isSame) { + return prev + } + // Ensure defaults are included even if backend omitted them + const merged = unique([...sanitized, ...defaults]) + return merged + }) + setSelectedMcpIds(agent.mcps ?? []) + }, [agent, availableTools]) + + const filteredTools = useMemo(() => { + if (!searchTerm.trim()) { + return availableTools + } + const term = searchTerm.trim().toLowerCase() + return availableTools.filter((tool) => { + return ( + tool.name.toLowerCase().includes(term) || + (tool.description ? tool.description.toLowerCase().includes(term) : false) + ) + }) + }, [availableTools, searchTerm]) + + const userAddedIds = useMemo(() => { + return approvedToolIds.filter((id) => !autoToolIds.includes(id)) + }, [approvedToolIds, autoToolIds]) + + const handleSelectPermissionMode = useCallback( + (nextMode: PermissionMode) => { + if (!agent || nextMode === selectedMode || isUpdatingMode) { + return + } + const defaults = computeModeDefaults(nextMode, availableTools) + const merged = unique([...defaults, ...userAddedIds]) + const removedDefaults = autoToolIds.filter((id) => !defaults.includes(id)) + + const applyChange = async () => { + setIsUpdatingMode(true) + try { + const nextConfiguration = { ...configuration, permission_mode: nextMode } satisfies AgentConfigurationState + await updateAgent({ id: agent.id, configuration: nextConfiguration, allowed_tools: merged }) + setConfiguration(nextConfiguration) + setSelectedMode(nextMode) + setAutoToolIds(defaults) + setApprovedToolIds(merged) + } finally { + setIsUpdatingMode(false) + } + } + + if (removedDefaults.length > 0) { + Modal.confirm({ + title: t('agent.settings.tooling.permissionMode.confirmChange.title', 'Change permission mode?'), + content: ( +
+

+ {t( + 'agent.settings.tooling.permissionMode.confirmChange.description', + 'Switching modes updates the automatically approved tools.' + )} +

+
+ {t('common.removed', 'Removed')}: +
    + {removedDefaults.map((id) => { + const tool = availableTools.find((item) => item.id === id) + return
  • {tool?.name ?? id}
  • + })} +
+
+
+ ), + okText: t('common.confirm'), + cancelText: t('common.cancel'), + onOk: applyChange + }) + } else { + void applyChange() + } + }, + [agent, selectedMode, isUpdatingMode, availableTools, userAddedIds, autoToolIds, configuration, updateAgent, t] + ) + + const handleToggleTool = useCallback( + (toolId: string, isApproved: boolean) => { + if (!agent || isUpdatingTools) { + return + } + setApprovedToolIds((prev) => { + const exists = prev.includes(toolId) + if (isApproved === exists) { + return prev + } + const next = isApproved ? [...prev, toolId] : prev.filter((id) => id !== toolId) + const sanitized = unique(next.filter((id) => availableTools.some((tool) => tool.id === id)).concat(autoToolIds)) + setIsUpdatingTools(true) + void (async () => { + try { + await updateAgent({ id: agent.id, allowed_tools: sanitized }) + } finally { + setIsUpdatingTools(false) + } + })() + return sanitized + }) + }, + [agent, isUpdatingTools, availableTools, autoToolIds, updateAgent] + ) + + const { agentSummary, autoCount, customCount } = useMemo(() => { + const autoCountValue = autoToolIds.length + const customCountValue = userAddedIds.length + return { + agentSummary: { + mode: selectedMode, + auto: autoCountValue, + custom: customCountValue, + totalTools: availableTools.length, + mcps: selectedMcpIds.length + }, + autoCount: autoCountValue, + customCount: customCountValue + } + }, [selectedMode, autoToolIds, userAddedIds, availableTools.length, selectedMcpIds.length]) + + const handleToggleMcp = useCallback( + (serverId: string, enabled: boolean) => { + if (!agent || isUpdatingMcp) { + return + } + setSelectedMcpIds((prev) => { + const exists = prev.includes(serverId) + if (enabled === exists) { + return prev + } + const next = enabled ? [...prev, serverId] : prev.filter((id) => id !== serverId) + setIsUpdatingMcp(true) + void (async () => { + try { + await updateAgent({ id: agent.id, mcps: next }) + const refreshed = await client.getAgent(agent.id) + const key = client.agentPaths.withId(agent.id) + mutate(key, refreshed, false) + } finally { + setIsUpdatingMcp(false) + } + })() + return next + }) + }, + [agent, isUpdatingMcp, client, updateAgent] + ) + + if (!agent) { + return null + } + + return ( + + + + {t('agent.settings.tooling.steps.permissionMode.title', 'Step 1 · Permission mode')} + +
+ {permissionModeCards.map((card) => { + const isSelected = card.mode === selectedMode + const disabled = card.unsupported + const showCaution = card.caution + return ( + handleSelectPermissionMode(card.mode)} + className={`border ${ + isSelected ? 'border-primary shadow-lg' : 'border-default-200' + } ${disabled ? 'opacity-60' : ''}`}> + +
+ {t(card.titleKey, card.titleFallback)} + + {t(card.descriptionKey, card.descriptionFallback)} + +
+ {disabled ? ( + + {t('common.coming_soon', 'Coming soon')} + + ) : isSelected ? ( + }> + {t('common.selected', 'Selected')} + + ) : null} +
+ + {t(card.behaviorKey, card.behaviorFallback)} + {showCaution ? ( + + + {t( + 'agent.settings.tooling.permissionMode.bypassPermissions.warning', + 'Use with caution — all tools will run without asking for approval.' + )} + + ) : null} + +
+ ) + })} +
+
+ + + + {t('agent.settings.tooling.steps.preapproved.title', 'Step 2 · Pre-approved tools')} + +
+ + +
+ {filteredTools.length === 0 ? ( +
+ {t('agent.settings.tooling.preapproved.empty', 'No tools match your filters.')} +
+ ) : ( + filteredTools.map((tool) => { + const isAuto = autoToolIds.includes(tool.id) + const isApproved = approvedToolIds.includes(tool.id) + return ( + + +
+ {tool.name} + {tool.description ? ( + {tool.description} + ) : null} +
+ {isAuto ? ( + + {t('agent.settings.tooling.preapproved.autoBadge', 'Added by mode')} + + ) : null} + {tool.type === 'mcp' ? ( + + {t('agent.settings.tooling.preapproved.mcpBadge', 'MCP tool')} + + ) : null} + {tool.requirePermissions ? ( + + {t( + 'agent.settings.tooling.preapproved.requiresApproval', + 'Requires approval when disabled' + )} + + ) : null} +
+
+ handleToggleTool(tool.id, value)} + /> +
+ {isAuto ? ( + + + {t( + 'agent.settings.tooling.preapproved.autoDescription', + 'This tool is auto-approved by the current permission mode.' + )} + + + ) : null} +
+ ) + }) + )} +
+
+
+ + + {t('agent.settings.tooling.steps.mcp.title', 'MCP servers')} +
+ + {t( + 'agent.settings.tooling.mcp.description', + 'Connect MCP servers to unlock additional tools you can approve above.' + )} + + {availableServers.length === 0 ? ( +
+ {t('agent.settings.tooling.mcp.empty', 'No MCP servers detected. Add one from the MCP settings page.')} +
+ ) : ( +
+ {availableServers.map((server) => { + const isSelected = selectedMcpIds.includes(server.id) + return ( + + +
+ {server.name} + {server.description ? ( + {server.description} + ) : null} +
+ handleToggleMcp(server.id, value)} + /> +
+
+ ) + })} +
+ )} +
+ + + {t('agent.settings.tooling.mcp.manageHint', 'Need advanced configuration? Visit Settings → MCP Servers.')} + +
+
+
+ + + {t('agent.settings.tooling.steps.review.title', 'Step 3 · Review')} + + +
+ + {t('agent.settings.tooling.review.mode', { + defaultValue: `Mode: ${selectedMode}`, + mode: selectedMode + })} + + + {t('agent.settings.tooling.review.autoTools', { + defaultValue: `Auto: ${autoCount}`, + count: autoCount + })} + + + {t('agent.settings.tooling.review.customTools', { + defaultValue: `Custom: ${customCount}`, + count: customCount + })} + + + {t('agent.settings.tooling.review.mcp', { + defaultValue: `MCP: ${agentSummary.mcps}`, + count: agentSummary.mcps + })} + +
+ + {t( + 'agent.settings.tooling.review.helper', + 'Changes save automatically. Adjust the steps above any time to fine-tune permissions.' + )} + +
+
+
+
+ ) +} + +export default AgentToolingSettings diff --git a/src/renderer/src/pages/settings/AgentSettings/index.tsx b/src/renderer/src/pages/settings/AgentSettings/index.tsx index a23b14a7e0..137e3ebd93 100644 --- a/src/renderer/src/pages/settings/AgentSettings/index.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/index.tsx @@ -9,9 +9,8 @@ import styled from 'styled-components' import AgentAdvanceSettings from './AgentAdvanceSettings' import AgentEssentialSettings from './AgentEssentialSettings' -import AgentMCPSettings from './AgentMCPSettings' import AgentPromptSettings from './AgentPromptSettings' -import AgentToolSettings from './AgentToolSettings' +import AgentToolingSettings from './AgentToolingSettings' import { AgentLabel } from './shared' interface AgentSettingPopupShowParams { @@ -23,7 +22,7 @@ interface AgentSettingPopupParams extends AgentSettingPopupShowParams { resolve: () => void } -type AgentSettingPopupTab = 'essential' | 'prompt' | 'tools' | 'mcps' | 'advance' | 'session-mcps' +type AgentSettingPopupTab = 'essential' | 'prompt' | 'tooling' | 'advance' | 'session-mcps' const AgentSettingPopupContainer: React.FC = ({ tab, agentId, resolve }) => { const [open, setOpen] = useState(true) @@ -56,12 +55,8 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag label: t('agent.settings.prompt') }, { - key: 'tools', - label: t('agent.settings.tools.tab', 'Pre-approved tools') - }, - { - key: 'mcps', - label: t('agent.settings.mcps', 'MCP Servers') + key: 'tooling', + label: t('agent.settings.tooling.tab', 'Tooling & permissions') }, { key: 'advance', @@ -96,8 +91,7 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag {menu === 'essential' && } {menu === 'prompt' && } - {menu === 'tools' && } - {menu === 'mcps' && } + {menu === 'tooling' && } {menu === 'advance' && } diff --git a/src/renderer/src/types/agent.ts b/src/renderer/src/types/agent.ts index 9a7172fb23..af6350d488 100644 --- a/src/renderer/src/types/agent.ts +++ b/src/renderer/src/types/agent.ts @@ -45,6 +45,7 @@ export type Tool = z.infer // ------------------ Agent configuration & base schema ------------------ export const AgentConfigurationSchema = z .object({ + // https://docs.claude.com/en/docs/claude-code/sdk/sdk-permissions#mode-specific-behaviors permission_mode: PermissionModeSchema.default('default'), // Permission mode, default to 'default' max_turns: z.number().default(10) // Maximum number of interaction turns, default to 10 })