## Description
Reorganise the Chat Preferences and Language Models admin pages, tighten
their copy, and give their model selects one loading and failure
pattern. This PR also carries the former #3 (model load states and
hidden models), which was folded in. Each UI change is one bullet.
### Chat Preferences
- Split the settings into two cards: a **Chat** card and a **Search**
card.
- Moved **Default Language** to the top of the Chat card, as a
horizontal row.
- Chat card order: Default Language, Chat Naming Model, Multi-Model
Generation, Deep Research, Chat Auto-Scroll.
- Search card order: Search Selection Model, Search Mode, Auto-Detect
Search Filters.
- Removed the "Model selection" heading; the two task-model rows now sit
in the cards.
- Renamed "Chat naming model" to **Chat Naming Model**, and "Search
selection model" to **Search Selection Model**, with new copy.
- Removed the **Reset** buttons and the **(Optional)** suffix from both
task-model rows.
- Added an unset row at the top of both task-model selects, above a
divider; picking it clears the setting:
- **Chat Naming Model:** "Chat Default". Naming only runs in chats, on
the chat's own model.
- **Search Selection Model:** "Global Default", described as "Each
chat's model". Selection also runs in the Search UI, where the unset
fallback is the workspace default model.
- Clicking a task-model row's title opens its select.
- Removed the **beta** tag from Multi-Model Generation.
- Renamed "Disable Default Chat" to **Always Start with an Agent**, with
new copy and a link to featured agents.
- Made "without logging in" bold in the Allow Anonymous Users
description.
- Moved **Temperature Control** and **Reasoning Control** to the
Language Models page.
- New copy for Default Language and Search Selection Model.
### Language Models
- Card order: Default Model, Auto Mode, Router Model, a divider,
Reasoning Control, Temperature Control, Hide Provider Grouping.
- New copy for Default Model, Reasoning Control and Temperature Control.
- **Auto Mode** stays locked until the router list has loaded with at
least one router, with a "No router models found" tooltip when there are
none. Turning it off is always possible.
- The **Router Model** row shows only when a router model exists, and
its select can be cleared.
- With Auto Mode on and no usable router (none chosen, cleared, or
deleted since), an inline error under the Router Model row reads "Choose
a router model to enable users to use the auto routing mode." With no
router models at all, the error sits under Auto Mode instead.
### Settings (personal)
- **Display Language** now shares one component with the workspace
Default Language. It starts with a **Global Default** row (the workspace
language as its description) above a divider; picking it clears the
override.
### Model selects (task models and Router Model)
- **Loading:** a `DropdownLoader` stands in for the select.
- **Load failure:** the select stays, disabled with the error border,
and a persistent error toast offers a Retry button. The two task-model
rows share one toast.
- **Hidden models** are never offered, except a hidden model already in
use.
### Across these pages
- Removed **success toasts** from Chat Preferences, Language Models
(including cost overrides), Settings and Decision Models' provider
delete. Failures still show an error toast. e2e helpers wait for the
save response instead of the toast.
### Code changes behind the UI
- `LanguageSelect` (`lib/app/components.tsx`) serves both language
pickers.
- `filterModelConfigurations` loses its `visibleOnly` option; every
caller now lists visible models only (plus `keep`).
- `SimpleModelSelector`: its Global Default row takes a `label`, always
shows a divider (also in an ungrouped list), and the select takes
`isError`.
- Opal `InputHorizontal` and `InputVertical` take `error` and `warning`
props for inputs outside Formik.
## Changes
### Language Models
No "router model" configured, "Auto Mode" disabled:
<img width="872" height="452" alt="image"
src="https://github.com/user-attachments/assets/3894cb0f-9805-4909-99e9-f2bfe3ed92ab"
/>
"Auto Mode" on and no "Router model" configured:
<img width="877" height="526" alt="image"
src="https://github.com/user-attachments/assets/2ba22737-ddc7-4836-9ed8-83d2106aa54f"
/>
"Auto Mode" on and "Router model" configured:
<img width="863" height="506" alt="image"
src="https://github.com/user-attachments/assets/748db170-376b-4955-8d73-0c5a064d8a2b"
/>
### Chat Preferences
A few things have just been moved around here. The "beta" tag was also
removed from "Multi-Model Generation".
<img width="877" height="688" alt="image"
src="https://github.com/user-attachments/assets/e21336a8-4667-4110-a11e-e98f0488e0ad"
/>
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Reorganizes the admin Chat Preferences and Language Models pages and
tightens their copy. Chat Preferences now splits into Chat and Search
cards, Temperature Control and Reasoning Control move to Language
Models, and the task-model rows are renamed to Chat Naming Model and
Search Selection Model.
**Behavior changes**
- Task-model selectors start with a Global Default row that clears the
designation; chat naming reads Chat Default and search selection keys
off the workspace default. Reset buttons and the Optional suffix are
gone.
- Auto Mode stays locked until router models load and can only be
enabled when a router exists; it can always be turned off. The Router
Model row only shows when a router exists, and its select is clearable
with an inline error when Auto Mode needs a router.
- Failed model loads keep the select, disabled with an error border, and
raise a persistent error toast with a retry button; loading shows a
dropdown-loader shimmer.
- "Disable Default Chat" is renamed to "Always Start with an Agent" with
new copy and a link to featured agents.
- Success toasts are removed from these pages, including cost overrides
and Settings, and Decision Models no longer toasts provider deletion;
failures still show an error toast. e2e helpers wait for the save
response instead.
- `LanguageSelect` is shared between the workspace Default Language and
the personal Display Language; the personal picker offers a Global
Default row.
- Model pickers no longer list hidden models; they show visible models
only, plus the currently selected value.
<sup>Written for commit d523995b8d.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16435?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="View guided diff"
src="https://www.cubic.dev/buttons/review-in-cubic-light.svg"></picture></a>
<a
href="https://www.cubic.dev/action/auto-fix/pr/onyx-dot-app/onyx/16435?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16435&source=description"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"><img
alt="Turn on auto-fix"
src="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
- **New Features**
- Chat Preferences separates chat and search settings into dedicated
sections.
- Chat-naming and search-selection models can be configured
independently, with a Global Default option that clears each
task-specific selection.
- Language-model settings include per-chat reasoning and temperature
controls, plus guidance for unavailable router models.
- Workspace and user language selection supports a Global Default
option.
- **Improvements**
- Model selectors exclude hidden models except when a hidden model is
already selected.
- Settings provide inline loading and error feedback; success
notifications are no longer shown for many actions.
- Updated terminology and translations, including the setting for
starting chats with an agent.
- Input layouts support explicit error and warning messages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Keep MCP setup and connection edits unsaved until authentication and
tool discovery succeed. In-form failures retain drafts and leave the
saved connection unchanged.
Changing Configuration again invalidates an older staged proposal.
Explicit Chat choices take priority over previous enable intents.
Successful authentication preserves unsaved tool edits. URL changes
clear old header values, and intentional OAuth navigation avoids a
discard warning. Remove unused legacy client wrappers and dead state.
Full-page OAuth failures require restarting authorization; this change
does not persist browser form drafts across redirects.
Setup failures use a neutral translated connection error when an
upstream response has no usable error detail. OAuth-specific errors and
safe backend details remain intact.
The shared browser-test helper follows staged setup responses and reads
the saved server ID after OAuth completes.
## How Has This Been Tested?
139 component tests and 49 selected service tests passed. Full
TypeScript checking/coverage, lint, formatting, and feature-map checks
passed. Backend transaction test limits are recorded in the parent PR.
Two focused Playwright helper checks passed for immediate connection and
pending authorization. A live local smoke test stopped at the existing
SSRF block on the local mock; security settings were unchanged. Full
browser CI is rerunning.
## Additional Options
- [ ] [Optional] Please cherry-pick this PR to the latest release
version.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Added staged MCP server setup with validation and tool discovery
before publishing.
* Preserved unsaved settings and tool selections during authentication.
* Added OAuth continuation for authentication-required setups.
* Added explicit control over enabling MCP servers for Chat.
* **Bug Fixes**
* Failed or cancelled authentication no longer overwrites saved
settings.
* Provider-denied OAuth requests now display the correct cancellation
state.
* Improved connection-failure messaging across supported languages.
* **Documentation**
* Updated MCP setup and OAuth lifecycle documentation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
The default assistant is one global persona. Many callers change its
tool list at the same time: the MCP setup modal, plugin forms, Chat
Preferences, and parallel e2e specs. The backend did not serialize these
writes.
Two failures came from this:
- `update_default_assistant_configuration` set `persona.tools = []` with
no lock. A concurrent write changed the `persona__tool` rows first, so
the flush failed with `StaleDataError` ("expected to delete N row(s);
Only M were matched"). The handler caught only `ValueError`, so the user
got a 500.
- Callers sent a full list built from a cached copy. A stale copy
dropped tools that another caller had just added, or failed with 400
"Tool with ID N not found" after another spec deleted its MCP server.
Changes:
- All writers of the default assistant's tool list now lock the persona
row (`SELECT ... FOR UPDATE`), the same way
`set_default_chat_tool__no_commit` from #16281 does. The full-list
update now changes `persona__tool` rows with core SQL, so a concurrent
delete cannot cause `StaleDataError`.
- New `PATCH /admin/default-assistant/tools` with `{tool_ids, enabled}`.
It adds or removes only the given tools. It uses the same RBAC as the
existing route (`FULL_ADMIN_PANEL_ACCESS`).
- `useDefaultAssistantConfiguration.updateTools` uses the new route.
This covers MCP enable, plugin forms and Chat Preferences toggles. The
full-list `PATCH /admin/default-assistant` still works, and the system
prompt editor still uses it.
- The default assistant router now raises `OnyxError` instead of
`HTTPException`.
This fixes the flaky e2e test
`web/tests/e2e/mcp/mcp_per_user_key.spec.ts:131`. The test enables Chat
for a new MCP server while other specs change the same tool list. The
MCP enable and the e2e helper `addToolsToDefaultAssistant` now send only
their own tool ids. The backend serializes them with the other writers,
so they no longer get a 500 or lose tools. The `tools_popover` and
`default_agent` specs used the same read-merge-write pattern. They now
use the helper too.
## How Has This Been Tested?
- New external-dependency-unit tests in
`backend/tests/external_dependency_unit/db/test_default_assistant_disabled_tool.py`,
with real threads and Postgres:
- Concurrent full-list updates. On `main` this test fails with
`StaleDataError: DELETE statement on table 'persona__tool' expected to
delete 6 row(s); Only 0 were matched`. With this change it passes (3
runs in a row).
- Concurrent targeted adds keep every tool, and a removal drops only the
given tools.
- The new endpoint adds tools and returns 400 for an unknown tool id.
- A default selection change leaves custom agent tools alone. A
full-list resend does not enable a stale legacy OpenAPI attachment.
These two replace mock-based unit tests that depended on the old ORM
code path.
- Backend unit tests: route contract manifest, OpenAPI availability, MCP
default Chat availability.
- Jest: all suites under `src/sections/plugins`, `src/sections/actions`,
`src/lib/agents` and `src/views/admin` pass (47 suites, 526 tests).
- `tsc --noEmit` on `web/` passes. Pre-commit passes.
## Additional Options
- [ ] [Optional] Please cherry-pick this PR to the latest release
version.
- [x] [Optional] Override Linear Check
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes races between concurrent writers of the default assistant's tool
list. Parallel callers (MCP setup, plugin forms, Chat Preferences, e2e
specs) could trigger a 500 from `StaleDataError`, silently drop tools
when one caller's stale cached list overwrote another's changes, or
deadlock when a selection write overlapped a custom tool update.
- All writers now lock the default persona row; the full-list update
replaces `persona__tool` rows with core SQL instead of an ORM
collection, so concurrent deletes no longer fail the flush.
- Custom tool create and update routes take the persona lock before
saving the tool, so selection writers and tool updates share one lock
order and cannot deadlock.
- Adds `PATCH /admin/default-assistant/tools` (`{tool_ids, enabled}`)
that only adds or removes the given tools, and switches MCP enable,
plugin forms, Chat Preferences toggles, and the e2e helper to it.
- Errors from the default assistant router now surface as `OnyxError`
instead of `HTTPException`.
<sup>Written for commit 70699450d1.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16336?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="View guided diff"
src="https://www.cubic.dev/buttons/review-in-cubic-light.svg"></picture></a>
<a
href="https://www.cubic.dev/action/auto-fix/pr/onyx-dot-app/onyx/16336?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16336&source=description"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"><img
alt="Turn on auto-fix"
src="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Added incremental tool management for the default assistant, allowing
selected tools to be enabled or disabled without replacing other
selections.
* Added validation for tool selections and clearer error responses for
invalid requests.
* **Bug Fixes**
* Improved reliability when multiple settings updates occur
simultaneously.
* Preserved tools added by other sessions during concurrent updates.
* Prevented unrelated assistant tool settings from being changed.
* **Documentation**
* Updated default-assistant configuration and tool-management
documentation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## Description
This PR fixes two Playwright test races.
**Project files visual regression**
(`chat/project_files_visual_regression.spec.ts`)
- The test needed a retry in almost every CI run.
- `beforeAll` uploads the file through the API. The page loads about 2 s
later and fetches the project details once, while the file is still
`PROCESSING`.
- The projects provider polls `/file/statuses` only for files that the
page itself uploaded. So "Processing..." never changes.
- Fix: `beforeAll` now polls `POST /api/user/projects/file/statuses`
until the file is `COMPLETED`, before any page loads. The new helper is
`OnyxApiClient.waitForUserFilesCompleted`. It fails if a file ends in a
different status or does not finish in time.
**Group-manager fixture cleanup** (`admin/group-manager/fixtures.ts`)
- Teardown logged `cleanup: TypeError: groups.find is not a function`.
- Teardown started the deletion of four cc_pairs, then called
`setGroupCcPairs`. That call lists the groups. The list endpoint returns
500 while a linked connector is being deleted.
- `getGroupUserIds` did not check the response, so it called `.find` on
the error body.
- Fix: teardown now detaches the foreign group and the managed group
from their cc_pairs before it deletes the cc_pairs. `getGroupUserIds`
now goes through `handleResponse`, so a failure gives a clear error.
**Not fixed here:** the list endpoint itself is a separate product bug.
`ConnectorSnapshot.from_connector_db_model`
(`backend/ee/onyx/server/user_group/models.py`) raises `AttributeError`
when a group still links a cc_pair whose connector is deleted. One such
group makes `GET /manage/admin/user-group` fail for all groups.
## How Has This Been Tested?
- Pre-commit passed on the changed files (TypeScript type check and
coverage, oxlint, oxfmt).
- I did not run the specs locally. The local backend returned 500 on
login during the Playwright global setup, so the specs could not start.
CI runs both specs.
## Additional Options
- [ ] [Optional] Please cherry-pick this PR to the latest release
version.
- [x] [Optional] Override Linear Check
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes two Playwright test races that caused flaky CI runs.
Project files visual regression: the file uploaded through the API in
`beforeAll` was still `PROCESSING` when the page loaded, and the page
only polls files it uploaded itself, so it showed "Processing..."
forever. `beforeAll` now waits on the file status endpoint until the
file is `COMPLETED` via the new
`OnyxApiClient.waitForUserFilesCompleted`.
Group-manager fixture teardown: `getGroupUserIds` called `.find` on a
500 error body, logging `groups.find is not a function`. Teardown now
detaches groups from their cc_pairs before deleting the cc_pairs, and
`getGroupUserIds` goes through `handleResponse` so failures surface
clearly.
The 500 from `GET /manage/admin/user-group` while a linked connector is
being deleted is a separate backend bug not fixed here.
<sup>Written for commit 388bad8179.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16333?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="View guided diff"
src="https://www.cubic.dev/buttons/review-in-cubic-light.svg"></picture></a>
<a
href="https://www.cubic.dev/action/auto-fix/pr/onyx-dot-app/onyx/16333?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16333&source=description"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"><img
alt="Turn on auto-fix"
src="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
- **Tests**
- Improved visual regression test reliability by validating uploads and
waiting for files to finish processing before execution.
- Added clearer handling and reporting for file-processing failures and
timeouts.
- Improved test cleanup to fully remove group and connector
associations.
- Strengthened API response validation used by automated tests.
- Updated testing documentation for file-processing utilities.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>