From 3b55497d68082149441550f8cdf3654785b5f256 Mon Sep 17 00:00:00 2001 From: JUN Date: Thu, 15 Oct 2026 22:40:09 +0900 Subject: [PATCH] fix(gui): fold Advisor into the Subagents row and collapse its notices Advisor no longer takes a sidebar row. It joins Subagents as a section-switcher member, the same pattern Usage & Logs uses, and keeps its #advisor route. The three cost and data-sharing warnings fold into one collapsed disclosure; the consent checkbox and its required notice stay visible. --- docs-site/src/content/docs/guides/web-dashboard.md | 2 +- .../content/docs/reference/configuration/advisor.md | 2 +- gui/src/i18n/de.ts | 1 + gui/src/i18n/en.ts | 1 + gui/src/i18n/fr.ts | 1 + gui/src/i18n/ja.ts | 1 + gui/src/i18n/ko.ts | 1 + gui/src/i18n/pt.ts | 1 + gui/src/i18n/ru.ts | 1 + gui/src/i18n/tr.ts | 1 + gui/src/i18n/vi.ts | 1 + gui/src/i18n/zh-TW.ts | 1 + gui/src/i18n/zh.ts | 1 + gui/src/nav-groups.ts | 10 +++++----- gui/src/pages/Advisor.tsx | 11 ++++++++--- gui/tests/sidebar-rows.test.ts | 9 +++++---- 16 files changed, 31 insertions(+), 14 deletions(-) diff --git a/docs-site/src/content/docs/guides/web-dashboard.md b/docs-site/src/content/docs/guides/web-dashboard.md index d808779cd34..fa094752c93 100644 --- a/docs-site/src/content/docs/guides/web-dashboard.md +++ b/docs-site/src/content/docs/guides/web-dashboard.md @@ -71,7 +71,7 @@ host and port over a LAN IP or an alias. ## Dashboard layout -The sidebar has eight rows, in order: **Dashboard**, **Connect**, **Codex**, **Providers**, **Models**, **Subagents**, **Usage & Logs**, and **Remote Link**. **Connect** opens the page headed **Connect**, with its tab strip ordered **Codex**, **Claude**, **Claude Desktop**, **Grok Build**, then the remaining integrations. Claude has no sub-tabs; it shows the Claude Code settings directly. Claude Desktop is a separate Connect tab at `#claude/desktop`. Connect has no section switcher. **Usage & Logs** opens Usage and groups Usage, Logs & Debug, and Storage with a pill-shaped section switcher. The last row, **Remote Link**, groups Remote Link (`#remote`) and Remote Workspace (`#remote-workspace`, when available) with the same switcher. **Codex** is the renamed **Codex Set** row; the preserved URLs include `#codex-set`, `#claude`, `#claude/code`, and `#claude/desktop`; the former `#claude/settings` bookmark opens Code. The former `#claude/account` bookmark redirects to `#providers?provider=anthropic&tab=accounts`. Startup (`#startup`) stays outside the sidebar. +The sidebar has eight rows, in order: **Dashboard**, **Connect**, **Codex**, **Providers**, **Models**, **Subagents**, **Usage & Logs**, and **Remote Link**. **Connect** opens the page headed **Connect**, with its tab strip ordered **Codex**, **Claude**, **Claude Desktop**, **Grok Build**, then the remaining integrations. Claude has no sub-tabs; it shows the Claude Code settings directly. Claude Desktop is a separate Connect tab at `#claude/desktop`. Connect has no section switcher. **Subagents** opens Subagents and groups Subagents and Advisor (`#advisor`) with a pill-shaped section switcher. **Usage & Logs** opens Usage and groups Usage, Logs & Debug, and Storage with a pill-shaped section switcher. The last row, **Remote Link**, groups Remote Link (`#remote`) and Remote Workspace (`#remote-workspace`, when available) with the same switcher. **Codex** is the renamed **Codex Set** row; the preserved URLs include `#codex-set`, `#claude`, `#claude/code`, and `#claude/desktop`; the former `#claude/settings` bookmark opens Code. The former `#claude/account` bookmark redirects to `#providers?provider=anthropic&tab=accounts`. Startup (`#startup`) stays outside the sidebar. Responses first-output timing includes streamed function arguments and custom-tool input, as well as text and reasoning. A tool-only turn can therefore have a first-output time even without prose. diff --git a/docs-site/src/content/docs/reference/configuration/advisor.md b/docs-site/src/content/docs/reference/configuration/advisor.md index d91410b6fe1..ce55f2e98b3 100644 --- a/docs-site/src/content/docs/reference/configuration/advisor.md +++ b/docs-site/src/content/docs/reference/configuration/advisor.md @@ -16,7 +16,7 @@ never sees — even a worker that never spawns anything can be advised. ## Configuration -On the dashboard **Advisor** page, choose the expert model from the catalog selector. It saves the exact catalog identifier, including native, provider, account-qualified, custom and combo selectors. Disabled models and models awaiting initial selection are not offered as new choices. A saved identifier missing from the selectable catalog remains visible with a warning and is preserved until you choose another model or clear it. Catalog loading failures preserve the selection and provide Retry. Choose a model that can return text advice: the catalog does not guarantee consultation compatibility. The CLI and API continue to accept router-supported model strings. +On the dashboard **Advisor** page (under the **Subagents** sidebar row), choose the expert model from the catalog selector. It saves the exact catalog identifier, including native, provider, account-qualified, custom and combo selectors. Disabled models and models awaiting initial selection are not offered as new choices. A saved identifier missing from the selectable catalog remains visible with a warning and is preserved until you choose another model or clear it. Catalog loading failures preserve the selection and provide Retry. Choose a model that can return text advice: the catalog does not guarantee consultation compatibility. The CLI and API continue to accept router-supported model strings. ```json { diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index 28e1c16d09f..f85103d56cc 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -145,6 +145,7 @@ export const de: Record = { "advisor.saved": "Beratereinstellungen gespeichert.", "advisor.loadFailed": "Beratereinstellungen konnten nicht geladen werden. Läuft der Proxy?", "advisor.warning.noModel": "Aktiviert, aber kein Expertenmodell konfiguriert — der Advisor kann erst nach der Modellkonfiguration ausgeführt werden.", + "advisor.notices": "Hinweise zu Kosten und Datenweitergabe", "advisor.costNote": "Konsultationen sind echte zusätzliche Modellaufrufe und erscheinen in der Nutzung unter dem Beratermodell, nicht dem Worker-Modell.", "advisor.privacyNote": "Hinweis zu mehreren Anbietern: Konsultationen senden die Aufgabenkonversation und Tool-Ergebnisse an den konfigurierten Berater-Anbieter, der sich vom Worker-Anbieter unterscheiden kann. Aufgabeninhalte werden nicht von Geheimnissen bereinigt — aktivieren Sie den Berater nicht bei Aufgaben, deren Inhalte Sie diesem Anbieter nicht anvertrauen würden. Das Einschalten allein zeichnet diese Zustimmung nicht auf.", "advisor.disclosure": "Eine Konsultation kann die letzte Nutzeraufgabe, geparsten Nutzer-/Assistenten-/Entwicklertext, Tool-Aufrufe und Argumente, Tool-Ergebnisse, den Tool-Katalog des Workers, die Worker-Identität, das konfigurierte Berater-Modell und eine optionale Fokusfrage senden. OpenCodex fügt keine Provider-API-Schlüssel, Authorization-Header, OAuth-Token, Backend-Geheimnisse, Prozessumgebung oder verborgenes Chain-of-Thought ein. Aufgabeninhalt wird nicht von Geheimnissen bereinigt: ein eingefügter Schlüssel, ein Geheimnis in einer Datei oder ein von einem Tool ausgegebenes Token kann mitgesendet werden. Der Berater-Anbieter kann vom Worker-Anbieter abweichen.", diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index b3b8af556c9..eac8d873bcd 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -146,6 +146,7 @@ export const en = { "advisor.saved": "Advisor settings saved.", "advisor.loadFailed": "Could not load advisor settings. Is the proxy running?", "advisor.warning.noModel": "Enabled but no expert model is configured yet — the Advisor cannot run until a model is configured.", + "advisor.notices": "Cost and data-sharing notices", "advisor.costNote": "Consultations are real extra model calls. Each one appears in usage under the advisor model, not the worker model.", "advisor.privacyNote": "Cross-provider notice: consultations send the task conversation and tool results to the configured advisor provider, which may differ from the worker's provider. Task content is not secret-redacted — do not enable the advisor on tasks whose content you would not share with that provider. Turning Advisor on does not itself record this consent.", "advisor.disclosure": "A consultation may send the latest user task, parsed user/assistant/developer text, tool calls and arguments, tool results, the worker tool catalog, worker identity, the configured Advisor model, and an optional focus question. OpenCodex does not insert provider API keys, authorization headers, OAuth tokens, backend secrets, process environment, or hidden chain-of-thought. Task content is not secret-redacted: a pasted key, a secret in a file, or a token printed by a tool can be sent. The Advisor provider may differ from the worker provider.", diff --git a/gui/src/i18n/fr.ts b/gui/src/i18n/fr.ts index c7a4178d22a..f96052ac287 100644 --- a/gui/src/i18n/fr.ts +++ b/gui/src/i18n/fr.ts @@ -143,6 +143,7 @@ export const fr: Record = { "advisor.saved": "Réglages du conseiller enregistrés.", "advisor.loadFailed": "Impossible de charger les réglages du conseiller. Le proxy tourne-t-il ?", "advisor.warning.noModel": "Activé mais aucun modèle expert configuré — l'Advisor ne peut pas s'exécuter tant qu'un modèle n'est pas configuré.", + "advisor.notices": "Avis sur les coûts et le partage des données", "advisor.costNote": "Les consultations sont de véritables appels de modèle supplémentaires, comptés dans l'usage sous le modèle conseiller, pas le modèle worker.", "advisor.privacyNote": "Avis multi-fournisseurs : les consultations envoient la conversation de tâche et les résultats d'outils au fournisseur conseiller configuré, qui peut différer de celui du worker. Le contenu de tâche n'est pas expurgé de secrets — n'activez pas le conseiller sur des tâches dont vous ne partageriez pas le contenu avec ce fournisseur. Activer le conseiller n'enregistre pas ce consentement.", "advisor.disclosure": "Une consultation peut envoyer la dernière demande, le texte utilisateur/assistant/développeur analysé, les appels d'outils et leurs arguments, les résultats d'outils, le catalogue d'outils du worker, l'identité du worker, le modèle conseiller configuré et une question de focus facultative. OpenCodex n'insère ni clés d'API, ni en-têtes Authorization, ni jetons OAuth, ni secrets de backend, ni environnement du processus, ni chaîne de pensée cachée. Le contenu de la tâche n'est pas expurgé : une clé collée, un secret dans un fichier ou un jeton imprimé par un outil peut être envoyé. Le fournisseur conseiller peut différer de celui du worker.", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index 429db62e793..34d911126c7 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -145,6 +145,7 @@ export const ja: Record = { "advisor.saved": "アドバイザー設定を保存しました。", "advisor.loadFailed": "アドバイザー設定を読み込めません。プロキシが実行中か確認してください。", "advisor.warning.noModel": "有効ですがエキスパートモデルが未設定です — モデルを設定するまで Advisor は実行されません。", + "advisor.notices": "コストとデータ共有に関する注意", "advisor.costNote": "相談は実際の追加モデル呼び出しであり、Worker ではなくアドバイザーモデルの使用量として記録されます。", "advisor.privacyNote": "クロスプロバイダーに関する注意:相談ではタスクの会話とツール結果が、設定されたアドバイザーのプロバイダー(ワーカーのプロバイダーと異なる場合があります)に送信されます。タスク内容のシークレット除去は行われません — そのプロバイダーに渡したくない内容のタスクでは有効にしないでください。有効化だけではこの同意は記録されません。", "advisor.disclosure": "相談では、最新のユーザー依頼、解析済みのユーザー/アシスタント/開発者テキスト、ツール呼び出しと引数、ツール結果、ワーカーのツール一覧、ワーカーの識別子、設定されたアドバイザーモデル、手動相談時の任意の焦点質問が送られることがあります。OpenCodex はプロバイダー API キー、Authorization ヘッダー、OAuth トークン、バックエンドの秘密、プロセス環境、隠された思考連鎖をプロンプトへは入れません。タスク内容の秘密は一般には除去されません。貼り付けた鍵、ファイル内の秘密、ツールが出力したトークンは送られることがあります。アドバイザーのプロバイダーはワーカーと異なる場合があります。", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index d4eb11a4621..adee4b1d4db 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -145,6 +145,7 @@ export const ko: Record = { "advisor.saved": "어드바이저 설정이 저장되었습니다.", "advisor.loadFailed": "어드바이저 설정을 불러올 수 없습니다. 프록시가 실행 중인지 확인하세요.", "advisor.warning.noModel": "활성화되었지만 전문가 모델이 설정되지 않았습니다 — 모델을 설정하기 전까지 Advisor는 실행되지 않습니다.", + "advisor.notices": "비용 및 데이터 공유 안내", "advisor.costNote": "상담은 실제 추가 모델 호출이며, Worker가 아닌 어드바이저 모델의 사용량으로 기록됩니다.", "advisor.privacyNote": "크로스 프로바이더 안내: 상담 시 작업 대화와 도구 결과가 설정된 어드바이저 프로바이더(워커의 프로바이더와 다를 수 있음)로 전송됩니다. 작업 내용에 대한 비밀 정보 제거는 수행되지 않습니다 — 해당 프로바이더와 공유하고 싶지 않은 내용의 작업에서는 활성화하지 마세요. 켜는 것만으로는 이 동의가 기록되지 않습니다.", "advisor.disclosure": "상담은 최신 사용자 요청, 파싱된 사용자/어시스턴트/개발자 텍스트, 도구 호출과 인자, 도구 결과, 워커 도구 목록, 워커 식별, 설정된 어드바이저 모델, 그리고 수동 호출의 선택적 초점 질문을 보낼 수 있습니다. OpenCodex는 프로바이더 API 키, Authorization 헤더, OAuth 토큰, 백엔드 비밀, 프로세스 환경, 숨겨진 사고 과정을 프롬프트에 넣지 않습니다. 작업 내용의 비밀은 일반적으로 제거되지 않습니다. 붙여 넣은 키, 파일 속 비밀, 도구가 출력한 토큰은 전송될 수 있습니다. 어드바이저 프로바이더는 워커와 다를 수 있습니다.", diff --git a/gui/src/i18n/pt.ts b/gui/src/i18n/pt.ts index ddcdcd753e8..eb2773da2bb 100644 --- a/gui/src/i18n/pt.ts +++ b/gui/src/i18n/pt.ts @@ -39,6 +39,7 @@ export const pt: Record = { "advisor.saved": "Configurações do Advisor salvas.", "advisor.loadFailed": "Não foi possível carregar as configurações do Advisor. O proxy está em execução?", "advisor.warning.noModel": "Ativado, mas sem modelo especialista configurado — o Advisor só pode executar consultas após configurar um modelo.", + "advisor.notices": "Avisos de custo e compartilhamento de dados", "advisor.costNote": "As consultas são chamadas adicionais reais ao modelo. Cada uma aparece no uso do modelo Advisor, e não do worker.", "advisor.privacyNote": "Aviso de compartilhamento entre provedores: as consultas enviam a conversa da tarefa e resultados de ferramentas ao provedor do Advisor, que pode ser diferente do provedor do worker. Segredos no conteúdo da tarefa não são removidos. Não ative o Advisor para conteúdo que você não compartilharia com esse provedor. Ativar o Advisor não registra esse consentimento.", "advisor.disclosure": "Uma consulta pode enviar a tarefa mais recente, textos de usuário/assistente/developer da conversa analisada, chamadas e argumentos de ferramentas, resultados, catálogo de ferramentas do worker, identidade do worker, modelo Advisor configurado e uma pergunta opcional. OpenCodex não insere chaves de API, cabeçalhos de autorização, tokens OAuth, segredos do backend, ambiente do processo ou raciocínio oculto. Segredos no conteúdo da tarefa não são removidos: uma chave colada, um segredo em um arquivo ou um token exibido por uma ferramenta pode ser enviado. O provedor do Advisor pode ser diferente do provedor do worker.", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index 528aaf28f46..3097c58863b 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -145,6 +145,7 @@ export const ru: Record = { "advisor.saved": "Настройки консультанта сохранены.", "advisor.loadFailed": "Не удалось загрузить настройки консультанта. Прокси запущен?", "advisor.warning.noModel": "Включено, но экспертная модель не настроена — Advisor не может работать, пока модель не настроена.", + "advisor.notices": "Предупреждения о стоимости и передаче данных", "advisor.costNote": "Каждая консультация — реальный дополнительный вызов модели; в использовании она учитывается под моделью консультанта, а не воркера.", "advisor.privacyNote": "Уведомление о межпровайдерной передаче: консультации отправляют беседу задачи и результаты инструментов настроенному провайдеру консультанта, который может отличаться от провайдера воркера. Содержимое задачи не очищается от секретов — не включайте консультанта для задач, содержимое которых вы не хотите передавать этому провайдеру. Само включение это согласие не записывает.", "advisor.disclosure": "Консультация может отправить последнюю просьбу пользователя, разобранный текст пользователя, ассистента и разработчика, вызовы инструментов и их аргументы, результаты инструментов, каталог инструментов воркера, идентификатор воркера, настроенную модель консультанта и необязательный уточняющий вопрос. OpenCodex не вставляет в запрос ключи API провайдера, заголовки Authorization, токены OAuth, секреты бэкенда, окружение процесса или скрытую цепочку рассуждений. Содержимое задачи от секретов не очищается: вставленный ключ, секрет в файле или токен, напечатанный инструментом, может быть отправлен. Провайдер консультанта может отличаться от провайдера воркера.", diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts index 73c61857c0c..ac9359b5d5c 100644 --- a/gui/src/i18n/tr.ts +++ b/gui/src/i18n/tr.ts @@ -145,6 +145,7 @@ export const tr: Record = { "advisor.saved": "Danışman ayarları kaydedildi.", "advisor.loadFailed": "Danışman ayarları yüklenemedi. Proxy çalışıyor mu?", "advisor.warning.noModel": "Etkin ancak uzman model yapılandırılmamış — model yapılandırılana kadar Advisor çalışmaz.", + "advisor.notices": "Maliyet ve veri paylaşımı uyarıları", "advisor.costNote": "Danışmalar gerçek ek model çağrılarıdır; kullanım, worker modeli değil danışman modeli altında görünür.", "advisor.privacyNote": "Sağlayıcılar arası uyarı: Danışmalar, görev konuşmasını ve araç sonuçlarını yapılandırılmış danışman sağlayıcısına (worker'ın sağlayıcısından farklı olabilir) gönderir. Görev içeriği sırlardan arındırılmaz — içeriğini bu sağlayıcıyla paylaşmak istemediğiniz görevlerde danışmanı etkinleştirmeyin. Açmak tek başına bu onayı kaydetmez.", "advisor.disclosure": "Bir danışma; son kullanıcı isteğini, ayrıştırılmış kullanıcı/asistan/geliştirici metnini, araç çağrılarını ve argümanlarını, araç sonuçlarını, worker araç kataloğunu, worker kimliğini, yapılandırılmış danışman modelini ve isteğe bağlı bir odak sorusunu gönderebilir. OpenCodex; sağlayıcı API anahtarlarını, Authorization başlıklarını, OAuth belirteçlerini, arka uç sırlarını, süreç ortamını veya gizli düşünce zincirini isteme koymaz. Görev içeriği sırlardan arındırılmaz: yapıştırılan bir anahtar, dosyadaki bir sır veya aracın yazdırdığı bir belirteç gönderilebilir. Danışman sağlayıcısı worker sağlayıcısından farklı olabilir.", diff --git a/gui/src/i18n/vi.ts b/gui/src/i18n/vi.ts index 3633f6a954d..081d3328201 100644 --- a/gui/src/i18n/vi.ts +++ b/gui/src/i18n/vi.ts @@ -144,6 +144,7 @@ export const vi: Record = { "advisor.saved": "Đã lưu cài đặt cố vấn.", "advisor.loadFailed": "Không thể tải cài đặt cố vấn. Proxy có đang chạy không?", "advisor.warning.noModel": "Đã bật nhưng chưa cấu hình mô hình chuyên gia — Advisor không thể chạy cho đến khi có mô hình.", + "advisor.notices": "Lưu ý về chi phí và chia sẻ dữ liệu", "advisor.costNote": "Mỗi lần tư vấn là một lời gọi mô hình thực sự bổ sung, được tính vào mức sử dụng theo mô hình cố vấn, không phải mô hình worker.", "advisor.privacyNote": "Lưu ý liên provider: các lần tư vấn gửi hội thoại nhiệm vụ và kết quả công cụ đến provider cố vấn đã cấu hình, có thể khác với provider của worker. OpenCodex không loại bỏ bí mật khỏi nội dung nhiệm vụ — không bật cố vấn cho nhiệm vụ mà bạn không muốn chia sẻ nội dung với provider đó. Bật cố vấn không tự ghi nhận sự đồng ý này.", "advisor.disclosure": "Một lần tư vấn có thể gửi yêu cầu người dùng mới nhất, văn bản người dùng/trợ lý/nhà phát triển đã phân tích, lệnh gọi công cụ và đối số, kết quả công cụ, danh mục công cụ của worker, danh tính worker, model cố vấn đã cấu hình, và câu hỏi trọng tâm tùy chọn. OpenCodex không đưa khóa API của provider, header Authorization, token OAuth, bí mật backend, môi trường tiến trình, hoặc chuỗi suy nghĩ ẩn vào prompt. Nội dung nhiệm vụ không được loại bỏ bí mật: khóa dán vào, bí mật trong tệp, hoặc token do công cụ in ra đều có thể được gửi. Provider cố vấn có thể khác provider của worker.", diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts index 15f5d4672c5..fcb3b353c05 100644 --- a/gui/src/i18n/zh-TW.ts +++ b/gui/src/i18n/zh-TW.ts @@ -137,6 +137,7 @@ export const zhTW: Record = { "advisor.saved": "顧問設定已儲存。", "advisor.loadFailed": "無法載入顧問設定。代理是否在執行?", "advisor.warning.noModel": "已啟用但尚未設定專家模型 — 設定模型之前 Advisor 無法執行。", + "advisor.notices": "費用與資料共享說明", "advisor.costNote": "每次諮詢都是真實的額外模型呼叫,會以顧問模型(而非 Worker 模型)計入用量。", "advisor.privacyNote": "跨 provider 提示:諮詢會把任務對話與工具結果傳送給設定的顧問 provider,它可能與 Worker 的 provider 不同。任務內容不做憑證脫敏 —— 若你不信任設定的顧問 provider 對這些任務內容的處理方式,請勿啟用顧問。只開啟開關並不會記錄這項同意。", "advisor.disclosure": "一次諮詢可能傳送:最新的使用者任務、已解析的使用者/助理/開發者文字、工具呼叫及其參數、工具結果、Worker 的工具目錄、Worker 身分、所設定的顧問模型,以及手動呼叫時的選用焦點問題。OpenCodex 不會把 provider API key、Authorization 標頭、OAuth token、後端密鑰、行程環境或隱藏的思維鏈寫進該提示。任務內容本身不做通用脫敏:貼進任務的金鑰、檔案裡的秘密、工具印出的 token 都可能被傳送。顧問 provider 可能與 Worker 的 provider 不同。", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index 2bfec913439..b554f8bed73 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -145,6 +145,7 @@ export const zh: Record = { "advisor.saved": "顾问设置已保存。", "advisor.loadFailed": "无法加载顾问设置。代理是否在运行?", "advisor.warning.noModel": "已启用但尚未配置专家模型 — 配置模型之前 Advisor 无法运行。", + "advisor.notices": "费用与数据共享说明", "advisor.costNote": "每次咨询都是真实的额外模型调用,会以顾问模型(而非 Worker 模型)计入用量。", "advisor.privacyNote": "跨 provider 提示:咨询会把任务对话与工具结果发送给配置的顾问 provider,它可能与 Worker 的 provider 不同。任务内容不做凭据脱敏 —— 若你不信任设定的顾问 provider 对这些任务内容的处理方式,请勿启用顾问。仅打开开关并不会记录这项同意。", "advisor.disclosure": "一次咨询可能发送:最新的用户任务、已解析的用户/助手/开发者文本、工具调用及其参数、工具结果、Worker 的工具目录、Worker 身份、所配置的顾问模型,以及手动调用时的可选焦点问题。OpenCodex 不会把 provider API key、Authorization 头、OAuth token、后端密钥、进程环境或隐藏的思维链写进该提示。任务内容本身不做通用脱敏:贴进任务的密钥、文件里的秘密、工具打印出的 token 都可能被发送。顾问 provider 可能与 Worker 的 provider 不同。", diff --git a/gui/src/nav-groups.ts b/gui/src/nav-groups.ts index 31921c9e0e2..9f078d49156 100644 --- a/gui/src/nav-groups.ts +++ b/gui/src/nav-groups.ts @@ -1,8 +1,8 @@ /** * The sidebar's rows, in order. * - * A row is a navigation group, not a page. Most rows hold one page; Usage & Logs and - * Remote Link each hold several existing pages, reached through the section switcher + * A row is a navigation group, not a page. Most rows hold one page; Subagents, Usage & Logs + * and Remote Link each hold several existing pages, reached through the section switcher * App renders above the page. Grouping changes no route: every member keeps the hash it * always had, so bookmarks and deep links still open the same page, and the row that * owns that page lights up. @@ -24,7 +24,6 @@ export type NavGroupId = | "providers" | "models" | "subagents" - | "advisor" | "usage-logs" | "remote"; @@ -42,8 +41,9 @@ export const NAV_GROUPS: readonly NavGroup[] = [ { id: "codex-set", tkey: "nav.codexSet", Icon: IconCodex, pages: ["codex-set"] }, { id: "providers", tkey: "nav.providers", Icon: IconServer, pages: ["providers"] }, { id: "models", tkey: "nav.models", Icon: IconBoxes, pages: ["models"] }, - { id: "subagents", tkey: "nav.subagents", Icon: IconBot, pages: ["subagents"] }, - { id: "advisor", tkey: "nav.advisor", Icon: IconBot, pages: ["advisor"] }, + // Advisor is another way a worker reaches a second model, so it sits beside Subagents + // in the switcher instead of taking a sidebar row of its own. + { id: "subagents", tkey: "nav.subagents", Icon: IconBot, pages: ["subagents", "advisor"] }, { id: "usage-logs", tkey: "nav.usageLogs", Icon: IconActivity, pages: ["usage", "logs", "storage"] }, { id: "remote", tkey: "nav.remote", Icon: IconMonitor, pages: ["remote", "remote-workspace"] }, ]; diff --git a/gui/src/pages/Advisor.tsx b/gui/src/pages/Advisor.tsx index 20978986a21..47bb6adb11d 100644 --- a/gui/src/pages/Advisor.tsx +++ b/gui/src/pages/Advisor.tsx @@ -221,9 +221,14 @@ export default function Advisor({ apiBase }: { apiBase: string }) {
{heading}

{t("advisor.description")}

- {t("advisor.costNote")} - {t("advisor.privacyNote")} - {t("advisor.disclosure")} + {/* Collapsed by default: three stacked warnings buried the settings below them. The + consent checkbox and its required notice stay visible, so folding them hides no gate. */} +
+ {t("advisor.notices")} + {t("advisor.costNote")} + {t("advisor.privacyNote")} + {t("advisor.disclosure")} +
{state.showSkeleton && {t("common.loading")}} {state.showError && !state.showSkeleton && ( diff --git a/gui/tests/sidebar-rows.test.ts b/gui/tests/sidebar-rows.test.ts index cda8874105d..fddb9119493 100644 --- a/gui/tests/sidebar-rows.test.ts +++ b/gui/tests/sidebar-rows.test.ts @@ -18,14 +18,14 @@ const raw = await Bun.file(new URL("../src/App.tsx", import.meta.url)).text(); */ const src = raw.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, ""); -test("the sidebar is nine group rows, in order, owning every sidebar page once", async () => { +test("the sidebar is eight group rows, in order, owning every sidebar page once", async () => { const { NAV_GROUPS, groupForPage } = await import("../src/nav-groups"); const { VALID_PAGES } = await import("../src/app-routing"); // The exact rows, in order. A count alone would pass if a row were swapped for // another, and Routing folding into Models is precisely that kind of change. expect(NAV_GROUPS.map(group => group.id)).toEqual([ - "dashboard", "connect", "codex-set", "providers", "models", "subagents", "advisor", "usage-logs", "remote", + "dashboard", "connect", "codex-set", "providers", "models", "subagents", "usage-logs", "remote", ]); expect(Object.fromEntries(NAV_GROUPS.map(group => [group.id, [...group.pages]]))).toEqual({ dashboard: ["dashboard"], @@ -34,8 +34,8 @@ test("the sidebar is nine group rows, in order, owning every sidebar page once", "codex-set": ["codex-set"], providers: ["providers"], models: ["models"], - subagents: ["subagents"], - advisor: ["advisor"], + // Advisor is a switcher member beside Subagents, not a row of its own. + subagents: ["subagents", "advisor"], // Usage leads, so the row opens Usage first. "usage-logs": ["usage", "logs", "storage"], // Last row, by request. @@ -50,6 +50,7 @@ test("the sidebar is nine group rows, in order, owning every sidebar page once", expect(new Set(owned)).toEqual(new Set([...VALID_PAGES].filter(page => page !== "startup"))); expect(groupForPage("startup")).toBeNull(); expect(groupForPage("storage")?.id).toBe("usage-logs"); + expect(groupForPage("advisor")?.id).toBe("subagents"); expect(groupForPage("claude")?.id).toBe("connect"); expect(groupForPage("remote-workspace")?.id).toBe("remote");