diff --git a/frontend/src/lib/components/copilot/chatModelSettings.test.ts b/frontend/src/lib/components/copilot/chatModelSettings.test.ts index 9088bd2405..18927069af 100644 --- a/frontend/src/lib/components/copilot/chatModelSettings.test.ts +++ b/frontend/src/lib/components/copilot/chatModelSettings.test.ts @@ -135,6 +135,15 @@ describe('carriedReasoning', () => { expect(carriedReasoning(REASONING_OFF, REASONING_OFF, cap('gpt-5'))).toBeUndefined() }) + // A provider the registry has no rules for draws no thinking control, so a carried level + // would be invisible and unclearable — and still sent, since an explicitly set effort + // goes out whatever the model. + it('drops the effort where it has no rules for the provider', () => { + expect( + carriedReasoning('high', REASONING_OFF, getReasoningCapability('customai', 'deepseek-r1')) + ).toBeUndefined() + }) + it('has nothing to carry when no effort is set', () => { expect(carriedReasoning(undefined, '', cap('gpt-5.1'))).toBeUndefined() expect(carriedReasoning('', '', cap('gpt-5.1'))).toBeUndefined() diff --git a/frontend/src/lib/components/copilot/chatModelSettings.ts b/frontend/src/lib/components/copilot/chatModelSettings.ts index 0dda8d0e2f..bbe6111270 100644 --- a/frontend/src/lib/components/copilot/chatModelSettings.ts +++ b/frontend/src/lib/components/copilot/chatModelSettings.ts @@ -85,17 +85,18 @@ export const REASONING_PROVIDER_DEFAULT = 'default' * rejects the request or quietly runs at another one, and the button would name a level the * run never used. Off survives only onto a model that can truly disable. * - * A model the registry has no rules for keeps whatever it had: dropping on `supported: - * false` would discard a real setting on the strength of never having heard of the - * provider, and nothing would draw a control to put it back. + * A model the registry has no rules for drops it too, for the same reason: the button draws + * no thinking control there, so a carried level would be invisible and unclearable while + * still going out on the wire — `resolveEffectiveReasoning` sends an explicitly set effort + * whatever the model, and a provider that rejects the field would then fail every turn with + * nothing on screen to explain it. */ export function carriedReasoning( current: string | undefined, offToken: string | undefined, - capability: { levels: string[]; canDisable: boolean; known: boolean } + capability: { levels: string[]; canDisable: boolean } ): string | undefined { if (current === undefined || current === '') return undefined - if (!capability.known) return current if (offToken !== undefined && current === offToken) { return capability.canDisable ? current : undefined }