diff --git a/src/components/settings/view/tabs/AutomationSettingsTab.dom.bun.test.tsx b/src/components/settings/view/tabs/AutomationSettingsTab.dom.bun.test.tsx index 386f5cb7..6769c873 100644 --- a/src/components/settings/view/tabs/AutomationSettingsTab.dom.bun.test.tsx +++ b/src/components/settings/view/tabs/AutomationSettingsTab.dom.bun.test.tsx @@ -318,3 +318,58 @@ test('browser routing wording keys have parity across all ten settings locales', for (const [key, text] of Object.entries(actual)) assert.ok(text.trim().length > 0, `${locale}${key}`); } }); + +/* + * The app overrides `mcp.discoveryMode`, `mcp.enableProjectConfig`, + * `tools.discoveryMode` and `astEdit.enabled` for every session. The overrides + * are the right call; saying nothing about them was not. "Works in the CLI, + * missing in the app, no error message" is an unanswerable support question. + */ + +test('the withheld runtime features are reported with a reason for each', async () => { + fakeApi(); + await mount(); + await waitFor(() => assert.equal(backendSelect().disabled, false)); + + assert.ok(screen.getByText(english.automation.withheld)); + assert.ok(screen.getByText(english.automation.withheldDescription)); + + for (const [label, reason] of [ + [english.automation.withheldMcp, english.automation.withheldMcpReason], + [english.automation.withheldToolDiscovery, english.automation.withheldToolDiscoveryReason], + [english.automation.withheldAstEdit, english.automation.withheldAstEditReason], + ]) { + assert.ok(screen.getByText(label), label); + assert.ok(screen.getByText(reason), reason); + } +}); + +test('the withheld block offers nothing to switch on', async () => { + fakeApi(); + await mount(); + await waitFor(() => assert.equal(backendSelect().disabled, false)); + + // A session cannot turn these on either, so a control here would be a lie. + const row = screen.getByText(english.automation.withheldMcp).closest('div')?.parentElement; + assert.ok(row); + assert.equal(row.querySelector('button, input, select'), null); +}); + +test('withheld-feature wording keys have parity across all ten settings locales', () => { + const keys = [ + 'withheld', 'withheldDescription', + 'withheldMcp', 'withheldMcpReason', + 'withheldToolDiscovery', 'withheldToolDiscoveryReason', + 'withheldAstEdit', 'withheldAstEditReason', + ] as const; + + for (const locale of ['en', 'ko', 'de', 'fr', 'it', 'ja', 'ru', 'tr', 'zh-CN', 'zh-TW']) { + const file = new URL(`../../../../i18n/locales/${locale}/settings.json`, import.meta.url); + const translated = JSON.parse(readFileSync(file, 'utf8')) as { automation?: Record }; + for (const key of keys) { + const text = translated.automation?.[key]; + assert.equal(typeof text, 'string', `${locale}.automation.${key}`); + assert.ok(String(text).trim().length > 0, `${locale}.automation.${key}`); + } + } +}); diff --git a/src/components/settings/view/tabs/AutomationSettingsTab.tsx b/src/components/settings/view/tabs/AutomationSettingsTab.tsx index 944ddbb5..127b42bb 100644 --- a/src/components/settings/view/tabs/AutomationSettingsTab.tsx +++ b/src/components/settings/view/tabs/AutomationSettingsTab.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from 'react'; -import { ExternalLink, RefreshCw, ShieldCheck, Trash2 } from 'lucide-react'; +import { ExternalLink, RefreshCw, ShieldCheck, SquareSlash, Trash2 } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { useAppShellStore } from '../../../../stores/useAppShellStore'; @@ -306,6 +306,34 @@ export default function AutomationSettingsTab() { ))} + + {/* + * The app overrides four runtime settings for every session: + * `mcp.discoveryMode`, `mcp.enableProjectConfig`, `tools.discoveryMode` + * and `astEdit.enabled`. Those overrides are a deliberate boundary and + * they stay - but until now nothing said so. A user whose MCP servers + * work in the GJC CLI found them simply absent here, with no error and + * no explanation, which is an unanswerable support question. + * + * Reports, not controls: there is nothing to toggle, because the point + * is that a session cannot toggle them either. + */} + + + {([ + ['mcp', 'automation.withheldMcp', 'automation.withheldMcpReason'], + ['toolDiscovery', 'automation.withheldToolDiscovery', 'automation.withheldToolDiscoveryReason'], + ['astEdit', 'automation.withheldAstEdit', 'automation.withheldAstEditReason'], + ] as const).map(([key, label, reason]) => ( + + + + {t('automation.notInstalled')} + + + ))} + + ); } diff --git a/src/i18n/locales/de/settings.json b/src/i18n/locales/de/settings.json index 5e9564f4..0e767e8f 100644 --- a/src/i18n/locales/de/settings.json +++ b/src/i18n/locales/de/settings.json @@ -169,7 +169,15 @@ "noGrants": "Keine gespeicherten Automatisierungsgenehmigungen.", "origin": "Herkunft der Website", "application": "Desktop-Anwendung", - "revoke": "Genehmigung entfernen" + "revoke": "Genehmigung entfernen", + "withheld": "In der App deaktivierte Laufzeitfunktionen", + "withheldDescription": "Die App bestimmt ihren eigenen Werkzeugsatz. Unabhängig von Ihrer GJC-Konfiguration bleiben diese Laufzeitfunktionen in jeder Sitzung aus.", + "withheldMcp": "MCP-Server", + "withheldMcpReason": "Server aus Ihren GJC-Einstellungen und eine .mcp.json im Projekt werden nicht geladen, damit keine Werkzeuge in eine Sitzung gelangen, die die App nie gewählt hat.", + "withheldToolDiscovery": "Werkzeugsuche", + "withheldToolDiscoveryReason": "Sitzungen können zur Laufzeit keine weiteren Werkzeuge aktivieren; der von der App gewählte Satz ist der gesamte Satz.", + "withheldAstEdit": "AST-Bearbeitung", + "withheldAstEditReason": "Sie zeigt Änderungen nur als Vorschau und wendet sie über ein Werkzeug an, das die App nicht bereitstellt — sie würde also Änderungen anbieten, die eine Sitzung nie übernehmen könnte." }, "notifications": { "title": "Hinweise", diff --git a/src/i18n/locales/en/settings.json b/src/i18n/locales/en/settings.json index 8bbbee4a..ea1bef88 100644 --- a/src/i18n/locales/en/settings.json +++ b/src/i18n/locales/en/settings.json @@ -184,7 +184,15 @@ "noGrants": "No saved automation approvals.", "origin": "Site origin", "application": "Desktop application", - "revoke": "Remove approval" + "revoke": "Remove approval", + "withheld": "Withheld runtime features", + "withheldDescription": "The app decides its own tool set. These runtime features stay off for every session, whatever your GJC configuration says.", + "withheldMcp": "MCP servers", + "withheldMcpReason": "Servers from your GJC settings and a project .mcp.json are not loaded, so tools the app never chose cannot reach a session.", + "withheldToolDiscovery": "Tool discovery", + "withheldToolDiscoveryReason": "Sessions cannot activate additional tools at runtime; the app-selected set is the whole set.", + "withheldAstEdit": "AST edit", + "withheldAstEditReason": "It only previews rewrites and applies them through a tool the app does not expose, so it would advertise edits a session could never commit." }, "notifications": { "title": "Notifications", diff --git a/src/i18n/locales/fr/settings.json b/src/i18n/locales/fr/settings.json index fb145f05..e5877420 100644 --- a/src/i18n/locales/fr/settings.json +++ b/src/i18n/locales/fr/settings.json @@ -169,7 +169,15 @@ "noGrants": "Aucune approbation d’automatisation enregistrée.", "origin": "Origine du site", "application": "Application de bureau", - "revoke": "Supprimer l'approbation" + "revoke": "Supprimer l'approbation", + "withheld": "Fonctions du runtime désactivées dans l'application", + "withheldDescription": "L'application décide de son propre jeu d'outils. Quelle que soit votre configuration GJC, ces fonctions du runtime restent désactivées dans toutes les sessions.", + "withheldMcp": "Serveurs MCP", + "withheldMcpReason": "Les serveurs de vos réglages GJC et un .mcp.json de projet ne sont pas chargés, afin qu'aucun outil que l'application n'a pas choisi n'atteigne une session.", + "withheldToolDiscovery": "Découverte d'outils", + "withheldToolDiscoveryReason": "Une session ne peut pas activer d'outils supplémentaires en cours d'exécution ; le jeu choisi par l'application est le jeu complet.", + "withheldAstEdit": "Édition AST", + "withheldAstEditReason": "Elle ne fait que prévisualiser les réécritures et les applique via un outil que l'application n'expose pas : elle annoncerait donc des modifications qu'une session ne pourrait jamais valider." }, "notifications": { "title": "Alertes", diff --git a/src/i18n/locales/it/settings.json b/src/i18n/locales/it/settings.json index 29a3660a..c0675b58 100644 --- a/src/i18n/locales/it/settings.json +++ b/src/i18n/locales/it/settings.json @@ -169,7 +169,15 @@ "noGrants": "Nessuna approvazione di automazione salvata.", "origin": "Origine del sito", "application": "Applicazione desktop", - "revoke": "Rimuovi l'approvazione" + "revoke": "Rimuovi l'approvazione", + "withheld": "Funzioni del runtime disattivate nell'app", + "withheldDescription": "L'app decide il proprio insieme di strumenti. Qualunque sia la tua configurazione GJC, queste funzioni del runtime restano disattivate in ogni sessione.", + "withheldMcp": "Server MCP", + "withheldMcpReason": "I server dalle tue impostazioni GJC e un .mcp.json di progetto non vengono caricati, così nessuno strumento che l'app non ha scelto raggiunge una sessione.", + "withheldToolDiscovery": "Rilevamento strumenti", + "withheldToolDiscoveryReason": "Le sessioni non possono attivare altri strumenti in esecuzione; l'insieme scelto dall'app è l'insieme completo.", + "withheldAstEdit": "Modifica AST", + "withheldAstEditReason": "Si limita ad anteprimare le riscritture e le applica tramite uno strumento che l'app non espone, quindi proporrebbe modifiche che una sessione non potrebbe mai confermare." }, "notifications": { "title": "Avvisi", diff --git a/src/i18n/locales/ja/settings.json b/src/i18n/locales/ja/settings.json index c41d2101..9e47e0bb 100644 --- a/src/i18n/locales/ja/settings.json +++ b/src/i18n/locales/ja/settings.json @@ -169,7 +169,15 @@ "noGrants": "保存された自動化の承認はありません。", "origin": "サイトのオリジン", "application": "デスクトップアプリケーション", - "revoke": "承認を削除" + "revoke": "承認を削除", + "withheld": "アプリで無効なランタイム機能", + "withheldDescription": "ツールセットはアプリが決定します。GJC の設定にかかわらず、以下のランタイム機能はすべてのセッションで無効です。", + "withheldMcp": "MCP サーバー", + "withheldMcpReason": "GJC 設定のサーバーとプロジェクトの .mcp.json は読み込まれません。アプリが選んでいないツールがセッションに届かないようにします。", + "withheldToolDiscovery": "ツール探索", + "withheldToolDiscoveryReason": "セッションは実行中に追加のツールを有効化できません。アプリが選んだ集合がすべてです。", + "withheldAstEdit": "AST 編集", + "withheldAstEditReason": "書き換えをプレビューするだけで、適用にはアプリが公開していないツールを使うため、セッションが決して確定できない編集を提示することになります。" }, "notifications": { "title": "通知", diff --git a/src/i18n/locales/ko/settings.json b/src/i18n/locales/ko/settings.json index 426ee22d..8a03a498 100644 --- a/src/i18n/locales/ko/settings.json +++ b/src/i18n/locales/ko/settings.json @@ -169,7 +169,15 @@ "noGrants": "저장된 자동화 승인 없음.", "origin": "사이트 원본", "application": "데스크톱 애플리케이션", - "revoke": "승인 취소" + "revoke": "승인 취소", + "withheld": "앱에서 비활성화된 런타임 기능", + "withheldDescription": "앱이 자체 도구 집합을 결정합니다. GJC 설정과 무관하게 아래 런타임 기능은 모든 세션에서 꺼져 있습니다.", + "withheldMcp": "MCP 서버", + "withheldMcpReason": "GJC 설정의 서버와 프로젝트의 .mcp.json을 불러오지 않습니다. 앱이 선택하지 않은 도구가 세션에 닿지 못하게 합니다.", + "withheldToolDiscovery": "도구 탐색", + "withheldToolDiscoveryReason": "세션이 실행 중에 도구를 추가로 활성화할 수 없습니다. 앱이 선택한 집합이 전부입니다.", + "withheldAstEdit": "AST 편집", + "withheldAstEditReason": "수정안을 미리 보여주기만 하고 적용은 앱이 노출하지 않는 도구로 합니다. 세션이 절대 커밋할 수 없는 편집을 광고하게 됩니다." }, "notifications": { "title": "알림 메시지", diff --git a/src/i18n/locales/ru/settings.json b/src/i18n/locales/ru/settings.json index 287cc206..ecae1c8f 100644 --- a/src/i18n/locales/ru/settings.json +++ b/src/i18n/locales/ru/settings.json @@ -169,7 +169,15 @@ "noGrants": "Нет сохраненных разрешений на автоматизацию.", "origin": "Происхождение сайта", "application": "Настольное приложение", - "revoke": "Удалить одобрение" + "revoke": "Удалить одобрение", + "withheld": "Отключённые в приложении возможности среды", + "withheldDescription": "Приложение само определяет набор инструментов. Независимо от вашей конфигурации GJC эти возможности среды отключены во всех сессиях.", + "withheldMcp": "Серверы MCP", + "withheldMcpReason": "Серверы из ваших настроек GJC и файл .mcp.json проекта не загружаются, чтобы в сессию не попали инструменты, которых приложение не выбирало.", + "withheldToolDiscovery": "Поиск инструментов", + "withheldToolDiscoveryReason": "Сессия не может включать дополнительные инструменты во время работы: выбранный приложением набор — это весь набор.", + "withheldAstEdit": "Редактирование AST", + "withheldAstEditReason": "Оно лишь показывает предпросмотр правок и применяет их инструментом, которого приложение не предоставляет, — то есть предлагало бы правки, которые сессия никогда не смогла бы применить." }, "notifications": { "title": "Центр уведомлений", diff --git a/src/i18n/locales/tr/settings.json b/src/i18n/locales/tr/settings.json index 86bf412a..9cc2c177 100644 --- a/src/i18n/locales/tr/settings.json +++ b/src/i18n/locales/tr/settings.json @@ -169,7 +169,15 @@ "noGrants": "Kaydedilmiş otomasyon onayı yok.", "origin": "Sitenin kökeni", "application": "Masaüstü uygulaması", - "revoke": "Onayı kaldır" + "revoke": "Onayı kaldır", + "withheld": "Uygulamada devre dışı çalışma zamanı özellikleri", + "withheldDescription": "Araç kümesine uygulama karar verir. GJC yapılandırmanız ne olursa olsun bu çalışma zamanı özellikleri her oturumda kapalıdır.", + "withheldMcp": "MCP sunucuları", + "withheldMcpReason": "GJC ayarlarınızdaki sunucular ve projedeki .mcp.json yüklenmez; böylece uygulamanın seçmediği araçlar bir oturuma ulaşamaz.", + "withheldToolDiscovery": "Araç keşfi", + "withheldToolDiscoveryReason": "Oturumlar çalışırken ek araç etkinleştiremez; uygulamanın seçtiği küme kümenin tamamıdır.", + "withheldAstEdit": "AST düzenleme", + "withheldAstEditReason": "Yalnızca yeniden yazımları önizler ve bunları uygulamanın sunmadığı bir araçla uygular; yani oturumun asla işleyemeyeceği düzenlemeleri duyurur." }, "notifications": { "title": "Uygulama bildirimleri", diff --git a/src/i18n/locales/zh-CN/settings.json b/src/i18n/locales/zh-CN/settings.json index 3c4b5f86..361e92af 100644 --- a/src/i18n/locales/zh-CN/settings.json +++ b/src/i18n/locales/zh-CN/settings.json @@ -169,7 +169,15 @@ "noGrants": "没有保存的自动化批准。", "origin": "站点来源", "application": "桌面应用", - "revoke": "移除批准" + "revoke": "移除批准", + "withheld": "应用中停用的运行时功能", + "withheldDescription": "工具集由应用决定。无论你的 GJC 配置如何,以下运行时功能在所有会话中都保持关闭。", + "withheldMcp": "MCP 服务器", + "withheldMcpReason": "不会加载你 GJC 设置中的服务器和项目里的 .mcp.json,以免应用从未选择的工具进入会话。", + "withheldToolDiscovery": "工具发现", + "withheldToolDiscoveryReason": "会话无法在运行时激活额外工具;应用选定的集合就是全部集合。", + "withheldAstEdit": "AST 编辑", + "withheldAstEditReason": "它只预览改写,并通过应用未开放的工具来应用,因此会展示会话永远无法提交的编辑。" }, "notifications": { "title": "通知", diff --git a/src/i18n/locales/zh-TW/settings.json b/src/i18n/locales/zh-TW/settings.json index 4c0aef85..cefc93b6 100644 --- a/src/i18n/locales/zh-TW/settings.json +++ b/src/i18n/locales/zh-TW/settings.json @@ -169,7 +169,15 @@ "noGrants": "無存儲自動化批准。", "origin": "網站原始碼", "application": "桌面應用程式", - "revoke": "移除批准" + "revoke": "移除批准", + "withheld": "應用程式中停用的執行階段功能", + "withheldDescription": "工具集由應用程式決定。無論你的 GJC 設定為何,以下執行階段功能在所有工作階段中都保持關閉。", + "withheldMcp": "MCP 伺服器", + "withheldMcpReason": "不會載入你 GJC 設定中的伺服器與專案中的 .mcp.json,以免應用程式從未選擇的工具進入工作階段。", + "withheldToolDiscovery": "工具探索", + "withheldToolDiscoveryReason": "工作階段無法在執行期間啟用額外工具;應用程式選定的集合就是全部集合。", + "withheldAstEdit": "AST 編輯", + "withheldAstEditReason": "它只預覽改寫,並透過應用程式未開放的工具套用,因此會展示工作階段永遠無法提交的編輯。" }, "notifications": { "title": "通知",