From 0c1dd1e96781fa41634d889d5673b03104d6014c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Mon, 5 Oct 2026 07:46:29 +0200 Subject: [PATCH] fix(coach): show the operator's central Codex when it answers The operator's shared Codex is appended to the resolved chain but is never a chain entry, so the Coach's provider select had no option for it and showed its first option as the one answering. The answering provider now appears as a disabled option, with a note that the operator manages the connection. --- messages/de.json | 4 ++- messages/en.json | 4 ++- messages/es.json | 4 ++- messages/fr.json | 4 ++- messages/it.json | 4 ++- messages/ko.json | 4 ++- messages/pl.json | 4 ++- .../__tests__/coach-settings-overlay.test.tsx | 21 +++++++++++-- .../coach-panel/coach-model-picker.tsx | 30 +++++++++++++++++++ 9 files changed, 70 insertions(+), 9 deletions(-) diff --git a/messages/de.json b/messages/de.json index e129a0e77..9beba5251 100644 --- a/messages/de.json +++ b/messages/de.json @@ -3138,7 +3138,9 @@ "saving": "Wird gespeichert…", "saveFailed": "Die Änderung ließ sich nicht speichern.", "allSettings": "Alle KI-Einstellungen", - "providerNotActive": "{provider} konnte nicht übernehmen: Es fehlt eine funktionierende Anmeldung oder ein Schlüssel. Richte das in den KI-Einstellungen ein." + "providerNotActive": "{provider} konnte nicht übernehmen: Es fehlt eine funktionierende Anmeldung oder ein Schlüssel. Richte das in den KI-Einstellungen ein.", + "operatorCodex": "ChatGPT (vom Betreiber bereitgestellt)", + "operatorManaged": "Diese Verbindung verwaltet der Betreiber deines Servers. Sobald einer deiner eigenen Anbieter eingerichtet ist, antwortet er zuerst." }, "answer": { "scrollToBottom": "Zur neuesten Nachricht", diff --git a/messages/en.json b/messages/en.json index 936b669a6..cba184481 100644 --- a/messages/en.json +++ b/messages/en.json @@ -3138,7 +3138,9 @@ "saving": "Saving…", "saveFailed": "That change could not be saved.", "allSettings": "All AI settings", - "providerNotActive": "{provider} could not take over: it has no working sign-in or key. Set it up in AI settings." + "providerNotActive": "{provider} could not take over: it has no working sign-in or key. Set it up in AI settings.", + "operatorCodex": "ChatGPT (shared by the operator)", + "operatorManaged": "Your server's operator manages this connection. Once one of your own providers is set up, it answers first." }, "answer": { "scrollToBottom": "Jump to the latest message", diff --git a/messages/es.json b/messages/es.json index 79a461607..c4f020aae 100644 --- a/messages/es.json +++ b/messages/es.json @@ -3138,7 +3138,9 @@ "saving": "Guardando…", "saveFailed": "No se pudo guardar el cambio.", "allSettings": "Todos los ajustes de IA", - "providerNotActive": "{provider} no pudo encargarse: no tiene un inicio de sesión ni una clave que funcionen. Configúralo en los ajustes de IA." + "providerNotActive": "{provider} no pudo encargarse: no tiene un inicio de sesión ni una clave que funcionen. Configúralo en los ajustes de IA.", + "operatorCodex": "ChatGPT (compartido por el operador)", + "operatorManaged": "El operador de tu servidor gestiona esta conexión. Cuando configures uno de tus propios proveedores, responderá primero." }, "answer": { "scrollToBottom": "Ir al último mensaje", diff --git a/messages/fr.json b/messages/fr.json index cbccefe22..b41d732f5 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -3138,7 +3138,9 @@ "saving": "Enregistrement…", "saveFailed": "Impossible d’enregistrer cette modification.", "allSettings": "Tous les paramètres IA", - "providerNotActive": "{provider} n'a pas pu prendre le relais : aucune connexion ni clé valide. Configurez-le dans les réglages IA." + "providerNotActive": "{provider} n'a pas pu prendre le relais : aucune connexion ni clé valide. Configurez-le dans les réglages IA.", + "operatorCodex": "ChatGPT (partagé par l'opérateur)", + "operatorManaged": "L'opérateur de votre serveur gère cette connexion. Dès qu'un de vos propres fournisseurs est configuré, c'est lui qui répond en premier." }, "answer": { "scrollToBottom": "Aller au dernier message", diff --git a/messages/it.json b/messages/it.json index 6e50a69ab..71753185c 100644 --- a/messages/it.json +++ b/messages/it.json @@ -3138,7 +3138,9 @@ "saving": "Salvataggio…", "saveFailed": "Impossibile salvare la modifica.", "allSettings": "Tutte le impostazioni IA", - "providerNotActive": "{provider} non è subentrato: manca un accesso o una chiave funzionante. Configuralo nelle impostazioni IA." + "providerNotActive": "{provider} non è subentrato: manca un accesso o una chiave funzionante. Configuralo nelle impostazioni IA.", + "operatorCodex": "ChatGPT (condiviso dall'operatore)", + "operatorManaged": "Questa connessione è gestita dall'operatore del tuo server. Quando configuri uno dei tuoi fornitori, risponde per primo." }, "answer": { "scrollToBottom": "Vai all'ultimo messaggio", diff --git a/messages/ko.json b/messages/ko.json index 4ecc63940..3751c5beb 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -3138,7 +3138,9 @@ "saving": "저장 중…", "saveFailed": "변경 사항을 저장하지 못했어요.", "allSettings": "모든 AI 설정", - "providerNotActive": "{provider}(으)로 전환하지 못했습니다. 작동하는 로그인이나 키가 없습니다. AI 설정에서 설정하세요." + "providerNotActive": "{provider}(으)로 전환하지 못했습니다. 작동하는 로그인이나 키가 없습니다. AI 설정에서 설정하세요.", + "operatorCodex": "ChatGPT (운영자 공유)", + "operatorManaged": "이 연결은 서버 운영자가 관리합니다. 내 공급자를 하나라도 설정하면 그 공급자가 먼저 답합니다." }, "answer": { "scrollToBottom": "최신 메시지로 이동", diff --git a/messages/pl.json b/messages/pl.json index b324f39b2..ae330d3b8 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -3138,7 +3138,9 @@ "saving": "Zapisywanie…", "saveFailed": "Nie udało się zapisać zmiany.", "allSettings": "Wszystkie ustawienia AI", - "providerNotActive": "{provider} nie mógł przejąć odpowiedzi: brak działającego logowania lub klucza. Skonfiguruj go w ustawieniach AI." + "providerNotActive": "{provider} nie mógł przejąć odpowiedzi: brak działającego logowania lub klucza. Skonfiguruj go w ustawieniach AI.", + "operatorCodex": "ChatGPT (udostępniony przez operatora)", + "operatorManaged": "Tym połączeniem zarządza operator Twojego serwera. Gdy skonfigurujesz własnego dostawcę, to on odpowie jako pierwszy." }, "answer": { "scrollToBottom": "Przejdź do najnowszej wiadomości", diff --git a/src/components/insights/coach-panel/__tests__/coach-settings-overlay.test.tsx b/src/components/insights/coach-panel/__tests__/coach-settings-overlay.test.tsx index 2441bfc05..ae0c02509 100644 --- a/src/components/insights/coach-panel/__tests__/coach-settings-overlay.test.tsx +++ b/src/components/insights/coach-panel/__tests__/coach-settings-overlay.test.tsx @@ -43,11 +43,11 @@ const PROVIDER: UserAIProvider = { compatReasoningEffort: null, }; -function render(node: React.ReactNode) { +function render(node: React.ReactNode, chain: ProviderChainData = CHAIN) { const client = new QueryClient({ defaultOptions: { queries: { retry: false } }, }); - client.setQueryData(queryKeys.insightsProviderChain(), CHAIN); + client.setQueryData(queryKeys.insightsProviderChain(), chain); client.setQueryData(queryKeys.userAiProvider(), PROVIDER); client.setQueryData(queryKeys.coachPrefs(), DEFAULT_COACH_PREFS); return renderToStaticMarkup( @@ -81,6 +81,23 @@ describe("", () => { expect(select).not.toContain('value="codex"'); }); + it("shows the operator's central Codex as the active provider when it answers", () => { + // `admin-codex` is never a chain entry, so no option matched it and the + // select showed its first option as the one answering. + const html = render(, { + ...CHAIN, + activeProvider: "admin-codex" as ProviderChainData["activeProvider"], + }); + const select = html.match( + /]*data-slot="coach-quick-provider"[\s\S]*?<\/select>/, + )?.[0]; + expect(select).toMatch( + / ) : null} + {unlisted ? ( + + ) : null} {options.map((p) => ( ))} + {unlisted === "admin-codex" ? ( +

+ {t("insights.coach.frame.operatorManaged")} +

+ ) : null} {model.kind === "preset" ? (