Commit Graph
4016 Commits
Author SHA1 Message Date
Raunak Bhagat 31461f9c8c feat(admin): reorganise chat preferences and language models settings (#16435)
## 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 -->
2026-10-22 01:47:24 +00:00
Nikolas Garza 5b69702c7e fix(web): indexing status and connector page fixes from the v5 bug bash (#16471)
## Description

Fixes on the indexing status page and the connector page from the v5 bug
bash. Stack 3 of 3, on #16469.

Fixes
[ENG-4400](https://linear.app/onyx-app/issue/ENG-4400/status-badges-skip-scheduled-and-count-a-deleting-connector-as-failed)
Fixes
[ENG-4439](https://linear.app/onyx-app/issue/ENG-4439/recently-indexed-adds-up-docs-across-runs-and-the-status-page-numbers)
Fixes
[ENG-4440](https://linear.app/onyx-app/issue/ENG-4440/status-page-search-returns-no-attempts-and-the-page-icon-beside-it)
Fixes
[ENG-4398](https://linear.app/onyx-app/issue/ENG-4398/charts-use-utc-while-runs-use-local-time-and-the-throughput-chart)
Fixes
[ENG-4399](https://linear.app/onyx-app/issue/ENG-4399/manage-index-has-no-perm-sync-trigger-or-re-index-on-the-detail-page)

**Why**
- Status badges skipped Scheduled, a deleting connector counted as
failed, and the doc figures added up every run with no labels.
- Charts read in the server's UTC while runs read local time, a clicked
bar drew nothing, Delete required a pause, and permission syncs had no
trigger.

**What**
- A `deleting` bucket end to end. Every nonzero bucket gets a badge, and
clicking one filters the list.
- Documents held is the main figure on the overview card and each row,
with processed counts labeled and explained on hover.
- Chart labels, tooltip and span pill format in the browser's time zone.
A picked slice keeps one neighbor each side so its bar draws.
- Manage Index: Delete works on an active pair behind the confirm, and
perm-synced pairs whose source syncs document permissions get a Sync
Permissions row, disabled while one is queued or running.
- The row search reads "Search run errors…", and the history panel
scrolls into view below the 1232px breakpoint.

## How Has This Been Tested?

Jest for the bucket tags, chart slices and time zone, the sync helper
and the panel reveal, plus a backend test for the deleting bucket. Live
in Chrome on the whole stack against a copy of a dev database: the
Deleting badge filtered to the one deleting pair and Failed no longer
counted it, chart labels read Pacific time, one clicked bar drew with
its span pill, Sync Permissions toasted and then showed disabled as
queued, and Delete was enabled on an active pair. Not live: the
narrow-window scroll, because the browser window would not resize. The
moving View Logs icon is unchanged, since keeping it in place needs a
layout change. The web servers ran with `TZ=UTC`, as in a container.

| Before | After |
|---|---|
| ![status page
before](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-stack-2026-10-21/before-status-page.png)
| ![status page
after](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-stack-2026-10-21/after-status-page.png)
|
| ![one picked bar draws
nothing](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-stack-2026-10-21/before-lone-bar.png)
| ![one picked bar
draws](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-stack-2026-10-21/after-lone-bar.png)
|
| ![Manage Index
before](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-stack-2026-10-21/before-manage-menu.png)
| ![Manage Index
after](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-stack-2026-10-21/after-manage-menu.png)
|

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.
2026-10-22 01:28:16 +00:00
Nikolas Garza aa6e8b3cfb fix(indexing): resolved and superseded runs close their errors, and a resolve clears the repeated-error flag (#16467)
## Description

Resolved and superseded runs stop counting as open errors, and resolving
a pair's latest failure clears its repeated-error flag. Stack 1 of 3.

Fixes
[ENG-4436](https://linear.app/onyx-app/issue/ENG-4436/retry-and-resolve-all-shown-write-two-different-resolved-flags)
Fixes
[ENG-4395](https://linear.app/onyx-app/issue/ENG-4395/errors-never-supersede-and-stay-open-forever)
Fixes
[ENG-4438](https://linear.app/onyx-app/issue/ENG-4438/failed-repeatedly-after-a-single-failure-and-the-warning-stays-after)

**Why**
- "Resolve all shown" wrote only `cc_pair_run_resolution`, while the
open doc error count read only `is_resolved`, so a resolved run still
said "N docs failed".
- Perm-sync and prune error runs never closed, a superseded run looked
the same as a manual resolve, and the "failed repeatedly" flag and alert
survived a resolve.

**What**
- A doc error is open only when `is_resolved` is false and its run has
no resolution. `is_resolved` is never bulk-written, since the poll
cursor and Retry read it.
- Full-pass kinds (permission sync, prune, group sync, checks) let a
later succeeded or failed run supersede earlier error runs. Indexing
keeps today's rule. A "Superseded" chip shows apart from "Resolved".
- Resolving the latest failed attempt clears `in_repeated_error_state`
and its alert. The beat heals a resolve that races it. Retry backoff is
unchanged, and a Cloud auto-pause stays.
- The status page row badge counts open errors, entity failures
included. Threshold-1 alerts say "failed", not "failed repeatedly".

## How Has This Been Tested?

External-dependency tests on a throwaway Postgres and Redis for
supersede rules per kind, resolve clearing the flag and alert, the beat
heal after a racing resolve, retry backoff after a resolve, and the
badge with an entity-only failure, plus unit tests for the threshold
copy. Each new test failed before its fix. Live in Chrome on the whole
stack against a copy of a dev database: "Resolve all shown" turned "1
doc failed" into "1 doc resolved", resolving a flagged pair cleared the
flag and its 725 alerts, and a completed-with-errors perm sync showed
"Superseded" once a clean sync followed. The window query's plan was
checked with EXPLAIN on synthetic data by a reviewer, not on a real
tenant.

| Before | After |
|---|---|
| ![resolved run still says
failed](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-stack-2026-10-21/before-resolved-card.png)
| ![resolved run counts items
resolved](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-stack-2026-10-21/after-resolved-card.png)
|
| ![superseded runs labeled
Resolved](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-stack-2026-10-21/before-superseded.png)
| ![superseded runs labeled
Superseded](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-stack-2026-10-21/after-superseded.png)
|

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.
2026-10-22 01:28:14 +00:00
Nikolas Garza 1541e3e593 fix(web): lock the SCIM and service-account pages without a license (#16470)
## Description

`/admin/scim` and `/admin/service-accounts` now redirect with "This
feature requires a license" when the license does not cover EE features,
like the other paid admin pages. Fixes
[ENG-4442](https://linear.app/onyx-app/issue/ENG-4442/scim-and-service-account-pages-are-not-locked-after-the-community).

**Why**
- Paid pages are locked by `web/src/proxy.ts:EE_ROUTES`, which rewrites
them to the `/ee` tree whose layout turns an unlicensed deployment away.
- These two paths were never in that list, so after a downgrade SCIM
offered a token button that returns 402, and service accounts showed a
load error.

**What**
- Both paths are added to `EE_ROUTES`.
- Like `/admin/groups`, each has a one-line page stub in `app/admin/`
(keeps typed `Link` hrefs valid) and `app/ee/admin/` (the copy the proxy
serves).
- The SCIM page body moves to `views/admin/ScimPage/`, with
`interfaces.ts` renamed `types.ts` and imports made absolute.
- `src/app/admin` type-coverage floor drops from 97.3 to 97.2 because
typed code left it. Feature-map docs list the new EE routes.

| Before | After |
| --- | --- |
|
![](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/lock-scim-and-service-accounts/before-scim.jpg)
|
![](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/lock-scim-and-service-accounts/after-scim.jpg)
|
|
![](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/lock-scim-and-service-accounts/before-service-accounts.jpg)
|
![](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/lock-scim-and-service-accounts/after-service-accounts.jpg)
|

## How Has This Been Tested?

Live in Chrome against a scratch API with no license
(`ee_features_enabled` false, tier community): on main both pages load
as above. On this branch both redirect to `/app` with the license toast,
the same as `/admin/groups`. With a license seeded, both pages still
load. Jest (`src/app/ee/__tests__/licenseGatedRoutes.test.ts`): the
proxy rewrites both paths to `/ee`, and both cases fail against main's
`proxy.ts`. `types:check` and pre-commit pass.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.
2026-10-22 01:24:21 +00:00
Raunak Bhagat f47f48e052 feat(opal): error toast actions, DropdownLoader and dropdown fixes (#16432)
## Description

Opal changes for the admin settings stack. Nothing in the app uses the
new pieces until the third PR in the stack (model load states).

- **Fixed:** a dropdown that is disabled while it saves no longer
flashes open when it is re-enabled. Disabling an open dropdown now
closes it, and its list always plays its close animation.
- **`LineLoader` takes a `height`:** each bar sits centred in a line of
that height, so the shimmer lines up with the text beside it. `height`
is a new `LineHeight` type, a closed Spacing step matching the
`--height-line-*` tokens (default 5, 20px). The Plugins pages' two-line
loaders become about 8px taller.
- **Toasts take an `id`:** a toast with the id of a live one replaces it
in place.
- **Toasts take an `action`:** an icon button left of the close button,
with its label as the tooltip. A click runs it, and the toast stays
open.
- **New `useErrorToast` hook:** shows a persistent error toast, with an
optional Retry button, while a fetch is failing. A retry that fails
again updates the toast in place, so it does not flash or animate in
again; it is removed when the error clears.
- **New `DropdownLoader`:** a select that has not loaded, drawn as the
input's shell with a shimmering icon and label and a static chevron.
- **Disabled and invalid inputs:** a disabled input with an invalid
field shows the error border.

## Changes

### Additional States

- error state
- disabled state
- error + disabled state

<img width="725" height="245" alt="image"
src="https://github.com/user-attachments/assets/2600f053-6b78-46d1-9376-abb022e3b024"
/>

### Loader edits

- Added dropdown-loaders
- Added variable line-heights to line-loaders


https://github.com/user-attachments/assets/6687b15e-c3ff-495b-bc41-f85d8a87d418

### Disabled while loading


https://github.com/user-attachments/assets/3eda28e3-b8a6-43cf-b3f7-e5b12dda7d55

## Summary by cubic
Reorganizes the Chat Preferences and Language Models admin pages and
standardizes how their settings behave. Chat Preferences is now split
into Chat and Search cards, Temperature and Reasoning Control move to
Language Models, and "Disable Default Chat" becomes "Always Start with
an Agent."

**Behavior changes**
- Model selects (Chat Naming, Search Selection, personal language) start
with a Global Default row naming the fallback; picking it clears the
override.
- Model pickers never list hidden models, except the current value.
- Auto Mode is disabled, with a tooltip, when no router model exists;
the Router Model row hides then.
- Success toasts are removed from these pages and Settings; failures
still toast, and model-load failures show a persistent error toast with
a retry button while the select stays, disabled with an error border.
- e2e helpers wait for the save response instead of the toast.

**Opal**
- Toasts accept an `id` (replacing a live toast) and an `action` button;
`useErrorToast` raises one for a failed fetch.
- Adds `DropdownLoader`, and `LineLoader` bars now sit centered in a
configurable `LineHeight`.
- Fixes a disabled dropdown flashing open on re-enable.

<sup>Written for commit f292221285.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16432?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/16432?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16432&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 accessible dropdown loading states with shimmer placeholders.
  - Added configurable loader heights for improved layout flexibility.
- Toasts now support action buttons and persistent error notifications
with optional retry actions.
  - Toasts with matching identifiers update in place.

- **Bug Fixes**
- Disabling an open dropdown now closes it with its normal exit
animation.
  - Disabled inputs with invalid fields now display an error border.
- Toast dismissal and replacement behavior now provide smoother
transitions and cleanup.

- **Documentation**
- Updated component guidance for dropdowns, loaders, and toast actions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-22 01:11:12 +00:00
roshan ae4ca216e9 feat(sidebar): tag Decision Models and Craft as Beta (#16440)
## Description

Adds a small blue **Beta** tag to Decision Models and Craft:

- **Admin sidebar, Decision Models:** the tag sits flush right on the
row (`SidebarTab` `rightChildren`).
- **Admin sidebar, Craft:** the tag sits flush right on the Craft
section heading, in the same column as the Decision Models tag. Access
and Preferences do not each get a tag.
- **App sidebar (`/app`), Craft row:** shows Beta when Craft is
available. The upsell state still shows only Business, so the row never
shows two tags.

Changes:

- `AdminRouteEntry` gets an optional `beta` flag. `DECISION_MODELS` sets
it, and `buildItems` copies it onto `SidebarItemEntry`.
- `BETA_SECTION_IDS` in `admin-sidebar-utils.ts` lists the sections with
a Beta heading (`craft`).
- Opal `SidebarLayouts.Section` gets an optional `tag?: TagProps`,
rendered at the end of the header (`.opal-sidebar-section__tag`),
aligned with tab `rightChildren`.
- Opal `SidebarTab`: `.opal-sidebar-tab__actions` no longer catches
clicks itself. Its direct children still do, except a passive `Tag` (no
`onClick`/`onRemove`), so a click on a passive tag opens the row link.
- New strings `sidebar.adminSidebar.betaTag.label` and
`sidebar.appSidebar.craft.betaTag` in all 9 locales.

Stacked on #16284.

## How Has This Been Tested?

- Jest: `admin-sidebar-utils.test.ts` checks that only Decision Models
is a beta entry; i18n catalog tests pass.
- Pre-commit: type check and coverage, oxfmt, oxlint, i18n copy check.
- UI at `898de231a3`, desktop 1440×1000 and mobile 390×844:
- The admin Decision Models and Craft heading tags have the same right
edge (216 px) and size (32×16 px) on both viewports.
- A click on the center of the Decision Models Beta tag opens
`/admin/decision-models`. A click on the `/app` Craft Beta tag opens the
Craft link (`GET /craft/v1`).
- `/app` shows only Beta when Craft is available, and only Business in
the upsell state.
- Regression: the chat-session `…` button in `rightChildren` still opens
its menu and does not navigate.
- Craft availability and tier were mocked locally in the browser
settings response, because the test workspace is Community tier. Real
Craft entitlement and the licensed Craft page were not tested.

Left: desktop. Right: mobile (390×844).

**Admin sidebar**

![Admin tag
alignment](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9iYTE5OThjZC02NmIzLTRiYTYtOTlmOC0yNTFkNGFmODM0OTEiLCJpYXQiOjE3OTI2MjY0NDAsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhZG1pbi1kZXNrdG9wLW1vYmlsZS5wbmcifQ.Mu5AWH20PifDbVH_ZErd5bwKLnQ0aXXsUPo76FS7FQA)

**`/app` Craft row**

| Craft available | Upgrade required |
|---|---|
| ![Craft
Beta](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS80MjA5MTkwOC03MGJjLTQzOGQtYjUyZS1hYjczZjMzM2JlYzgiLCJpYXQiOjE3OTI2MjY0NDAsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhcHAtYmV0YS1kZXNrdG9wLW1vYmlsZS5wbmcifQ.PPpDOLhL4ZoWOXNLvL2b_ibuuy53Cz4J9naVY-bJJ-M)
| ![Craft
Business](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS82MmY0OGNmNS0wZjRlLTQ3MTAtYTBhMS0yOTdjZDEzMmE0OWIiLCJpYXQiOjE3OTI2MjY0NDAsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhcHAtdXBzZWxsLWRlc2t0b3AtbW9iaWxlLnBuZyJ9.6nShf5pkKpT8QRiYsYU9guSScPRktZYizcmWEqVLwB8)
|

**Tag clicks and chat-menu regression**

![Tag
clicks](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9jMjFmOTQ3Ni0yNzU2LTRhYTUtODRlZi1iMTNlZGFiNjc3MzgiLCJpYXQiOjE3OTI2MjY0NDAsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJzaWRlYmFyLXRhZy1jbGlja3Mud2VicCJ9.f3txb6_u_GjmCAbQj3dLjnDNP0eBOyzoZS_b46Zo5jU)

![Chat menu still
opens](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8zY2U0MDQ2ZS0wOWQxLTRmMzQtYTIxYS0wNzA1YjhmMTVhODUiLCJpYXQiOjE3OTI2MjY0NDAsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJyZWdyZXNzaW9uLWNoYXQtbWVudS1sYWJlbGVkLnBuZyJ9.9L02NeKFBESHzkgtyzBCdC2nL6joFH6_62xR72D9v8s)

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.

Link to Devin session:
https://app.devin.ai/sessions/39d77bde91c34b75922bb05c02b93b90
Open in Devin Desktop:
https://app.devin.ai/desktop/session/39d77bde91c34b75922bb05c02b93b90?variant=devin
Requested by: @rohoswagger

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

- **New Features**
- Added Beta labels to relevant admin sidebar sections and items,
including Decision Models.
- Added Beta labeling to the Craft sidebar entry alongside its existing
Business label.
  - Added support for optional tags in sidebar section headers.
  - Localized new Beta labels across supported languages.
- **Bug Fixes**
- Sidebar tags no longer block clicks on their underlying navigation
items.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-22 01:02:09 +00:00
roshan ab944e79ac feat(llm): add Vercel AI Gateway decision support (#16284)
## Summary

Expose Vercel AI Gateway decision models on **Admin > Decision Models**
using the existing Gateway connection.

Treat catalog entries with type `evaluation` as decision-only models.
Verify their capability during save and connection testing. Send typed
requests to `/v1/decisions`, including when a custom API base omits
`/v1`.

Language models retain their chat behavior. Decision-model selection
remains opt-in and unset by default. No product workflow consumes the
selected model yet.

## Validation

- 35 focused Gateway and decision backend tests passed during
implementation.
- After the custom-base URL fix, all 29 decision tests passed.
Regression tests inspect outgoing HTTP URLs for versioned, unversioned,
and trailing-slash bases.
- Nine shared-setup frontend tests, type checks, lint, and touched-file
hooks passed.
- The live Gateway catalog reports decision models as `evaluation`.
- Current-head browser inspection confirmed the provider card and setup
form. No Gateway key was entered, so live credential validation and a
decision request were not run.

## Screenshots

Provider page at `2ffd035`, with selection unset.

![Vercel AI Gateway on Decision
Models](https://github.com/user-attachments/assets/e7650c3b-c94f-4454-8cb4-27623cd38988)

Gateway setup with empty credentials. Model discovery requires a key.

![Vercel AI Gateway decision-provider
setup](https://github.com/user-attachments/assets/0988c53d-dcad-4ac1-bffa-d4d3100f5b80)

## Stack testing (`29eec61fb4`)

Tested the full stack at the top commit on a local dev stack, desktop
and 390×844 mobile. OpenAI decisions and chat used a real key. TypeSafe,
Zoom, OpenRouter, and Vercel AI Gateway used local mock upstreams; Onyx
auth, validation, persistence, encryption, and UI were real.

- Passed: Decision Models lists only `evaluation` catalog entries;
save/test reject chat-only Gateway models.
- Passed: an unversioned base (`.../gateway`) sends `/gateway/v1/models`
and `/gateway/v1/decisions`.
- Passed: Gateway refresh and chat auto-mode do not add decision
selections; selected-model guard works.
- Passed (regression): Gateway chat models stay selectable and complete
a chat; evaluation models are absent from the chat picker.
- Fixed in #16276 (`37c248ec04`): the configured heading no longer shows
literal asterisks (retested at `78cf4ccb12`).

![Gateway desktop and
mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS80MmExMjFlZC1iYmVkLTRkNDctOTVjZC0yN2U2ZTQ4YWY3MDciLCJpYXQiOjE3OTI2MTIwNjQsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJnYXRld2F5LWRlc2t0b3AtbW9iaWxlLnBuZyJ9.kKxB5wNWO0G17mBCe0LGf9SYdGp6pQ1cu2JZbpyCp2c)

![Gateway decision
flow](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9iMGM2MjI4Ny0zMzE2LTQyYzUtYTZhZS1hOTUwZWVkNmM3NmIiLCJpYXQiOjE3OTI2MTIwNjQsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJnYXRld2F5LWZsb3cud2VicCJ9.wXIKOg0bIrNp5ogCX4W1behWh8jknqw7R8jOmtnjZVw)

![Gateway chat
regression](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS83Y2FmMTI5Zi1kMWIzLTQzZWMtYTk3Ni1mYzZjOGRiZjJkM2QiLCJpYXQiOjE3OTI2MTIwNjQsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJyZWdyZXNzaW9uLWdhdGV3YXktY2hhdC5wbmcifQ.zNspO70mI9HGCch4Bs05LTKlozAlovD_Rp-g58Mfn6c)

![Chat picker excludes evaluation
models](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS81MjU1YzEwMC03N2NjLTRmYjEtODRmZi1mMDZjOGI3NTlhNTkiLCJpYXQiOjE3OTI2MTIwNjQsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJyZWdyZXNzaW9uLXBpY2tlci1uby1ldmFsdWF0aW9uLnBuZyJ9.BAil9jq61tmJDuhX6d5U-vXEVsJNZ07otD--M3B8HVI)

## Stack

PR 4 of 4: #16262 → #16276 → #16280 → **#16284**.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **New Features**
  * Added Vercel AI Gateway as a supported decision-model provider.
* Gateway discovery now distinguishes chat-capable and decision-capable
models, including decision-only catalogs.
* Added automatic decision endpoint handling for custom Gateway base
URLs.
* Decision model configuration now lists Vercel AI Gateway as an
available provider.

* **Improvements**
  * Model selection prioritizes compatible models based on context.
  * Provider configuration controls adapt to scoped model settings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Link to Devin session:
https://app.devin.ai/sessions/39d77bde91c34b75922bb05c02b93b90
Open in Devin Desktop:
https://app.devin.ai/desktop/session/39d77bde91c34b75922bb05c02b93b90?variant=devin
Requested by: @rohoswagger
2026-10-22 01:02:08 +00:00
roshan fcc0cae05a feat(llm): share Zoom setup across Voice and Decisions (#16280)
## Summary

Add Zoom Decisions and Zoom Decisions Pro to **Admin > Decision
Models**.

Configure Zoom from either Voice or Decision Models. Both pages use one
shared connection and one encrypted API key and secret. Zoom Decisions
is opt-in: a Zoom decision provider exists only after an admin connects
Zoom on Decision Models. A Voice-only setup never creates one. Decision
setup does not activate Voice defaults or select a decision model.

Read current credentials and sign a fresh JWT for each request. Keep
Zoom decision models out of chat. Removing Zoom from Decision Models
deletes only the decision provider and keeps Scribe. Disconnecting Zoom
on Voice also removes its linked decision provider, after selected-model
checks. The migration only adds the link column; it writes no rows.

Both setup links open the [Zoom AI Services
documentation](https://developers.zoom.us/docs/ai-services/).

## Validation

- 336 focused backend tests and nine frontend component tests passed.
- Type checks, lint, translations, deterministic migration checks, and
touched-file hooks passed.
- A live Zoom Decision API request returned HTTP 200 with a JWT signed
using the existing Scribe credentials.
- The current-head preview opens the credential form directly from
Decision Models. No Voice setup is required first.
- PostgreSQL tests cover opt-in linking from Decision Models only,
rotation, rollback, relink and duplicate-link rejection, and deletion.

## Screenshot

Shared Zoom setup from Decision Models at `9b7792d`. Credentials are
empty. This screenshot demonstrates the form, not a completed connection
test.

![Shared Zoom AI Services
setup](https://github.com/user-attachments/assets/418e5eca-edd4-48c2-a0d4-a7103751c0cb)

## Stack testing (`29eec61fb4`)

Tested the full stack at the top commit on a local dev stack, desktop
and 390×844 mobile. OpenAI decisions and chat used a real key. TypeSafe,
Zoom, OpenRouter, and Vercel AI Gateway used local mock upstreams; Onyx
auth, validation, persistence, encryption, and UI were real.

- Fixed during testing: migration `9071f636d5f2` now revises
`4d8453213439` (#16351 on main). The old `down_revision` left two
alembic heads and failed most CI jobs on this PR and #16284.
- Passed: Voice-only Zoom setup creates no decision provider. Decision
Models opens the shared credential form directly.
- Passed: one encrypted Voice connection; the linked LLM provider stores
no duplicate key. JWT validation and the Scribe WebSocket handshake
worked against the mock.
- Passed: bad credentials fail. Both setup links open the Zoom AI
Services docs.
- Passed: selection guards block Zoom removal and Voice disconnect.
Removing Zoom from Decision Models keeps Voice/Scribe. Zoom decision
models are absent from chat.
- **Fixed in #16276 (`37c248ec04`):** after Zoom is disconnected on
Voice, sidebar navigation to Decision Models now shows **Connect**
without a reload (retested at `78cf4ccb12`). Before the fix, the card
showed **Configure/Delete** from a stale `adminLlmProvidersPaged` SWR
cache.

![Before the fix: stale Zoom card vs. after
reload](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9iMmMyNWEwZS0wNWVhLTQyYmUtODFmMy05YWFkYzQwYmYzNzciLCJpYXQiOjE3OTI2MTIwNjEsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJ6b29tLXN0YWxlLWJlZm9yZS1hZnRlci5wbmcifQ.Y0FiVPDvizzJT-GX494OGq6lsQoIrwMd7WHrwFzBWqQ)

![Zoom on Decision Models, desktop and
mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8wZWJiYmUxOC03Mjk5LTQ1ZTYtOGE2MC1iYjAzNThhZTgyMzgiLCJpYXQiOjE3OTI2MTIwNjEsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJ6b29tLWRlc2t0b3AtbW9iaWxlLnBuZyJ9.-EcWczrz3n2IdSdZR2J8WWS3-hl0q8qxwVvoq3K6hOU)

![Zoom on Voice, desktop and
mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8yMGUwMWVlNi0wYzMwLTQxOTItOWU3My01NDJlMTY0YzczM2QiLCJpYXQiOjE3OTI2MTIwNjEsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJ6b29tLXZvaWNlLWRlc2t0b3AtbW9iaWxlLnBuZyJ9.bJbNbAOJVKDduNobJUs-67TDqIwVssOhfATOxKx8usc)

![Zoom shared flow and stale card (before the
fix)](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS84NDk2ZWI1ZS1hYTVlLTQyN2EtYTllNC01YWExZjVhNTI1NWIiLCJpYXQiOjE3OTI2MTIwNjEsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJ6b29tLXNoYXJlZC1mbG93LWFuZC1zdGFsZS1jYXJkLndlYnAifQ.BVuTJXEo4tadBG-oIc9li-9eTNJDFmEHnlXc-zWy_HM)

![After the fix: Voice disconnect then Decision
Models](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8wZDg2MzIzYS1jZmQ1LTQyYzYtYjRiNy1mMmRiYWM4NWFiNDEiLCJpYXQiOjE3OTI2MTIwNjEsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJjYWNoZS1uYXZpZ2F0aW9uLndlYnAifQ.aElIlomXJ3AkNjokrig55oTDvAd1FONQuUsCxyqbM-U)

## Stack

PR 3 of 4: #16262 → #16276 → **#16280** → #16284.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added Zoom AI Decisions with two models supporting text and image
inputs.
* Added shared Zoom AI Services setup across Voice and Decision Models,
including connection selection, credential reuse, rotation, and removal.
* Added provider discovery, model capability indicators,
decision-specific validation, and localized interface text.
  * Renamed Zoom Scribe setup labels to Zoom AI Services.

* **Bug Fixes**
  * Improved connection validation and rollback.
* Improved credential handling and protection against deleting active
shared connections.
* Preserved existing shared connection settings when updating providers.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Link to Devin session:
https://app.devin.ai/sessions/39d77bde91c34b75922bb05c02b93b90
Open in Devin Desktop:
https://app.devin.ai/desktop/session/39d77bde91c34b75922bb05c02b93b90?variant=devin
Requested by: @rohoswagger
2026-10-22 01:02:08 +00:00
Nikolas Garza a0c949df51 fix(mcp): refuse OAuth connect when the provider rejects the client metadata document (#16460)
## Description

The MCP OAuth modal now refuses the connect when the provider rejects
Onyx's client metadata document. Before, the user landed on the
provider's error page. Fixes
[ENG-4412](https://linear.app/onyx-app/issue/ENG-4412/notion-mcp-connect-lands-on-a-bad-page-instead-of-refusing-the).

**Why**
- Auto-discovery uses CIMD whenever the provider supports it
([#13877](https://github.com/onyx-dot-app/onyx/pull/13877)). If the
provider cannot fetch `WEB_DOMAIN/api/mcp/oauth/client-metadata`
(private or IP-allowlisted Onyx, such as st-dev behind its WAF
allowlist), `/authorize` answers with an error and the user is stuck
there.
- Onyx never sees that failure, and the switch from
[#16227](https://github.com/onyx-dot-app/onyx/pull/16227) is easy to
miss.

**What**
- Before it redirects with a CIMD client_id, the backend requests the
authorization URL and reads only the status (SSRF-guarded, no redirects,
5s cap). A 4xx or 5xx raises `client_metadata_rejected`. Network errors
fail open.
- The check runs in both connect paths: server OAuth connect and the new
staged setup (Add MCP).
- `oauth_setup_error` passes nested `OnyxError`s through the SDK's
exception groups.
- The modal shows the error and a button that turns off "Use client
metadata document". Copy is in all locales.
- DCR and known-provider flows are unchanged.

| Before | After |
| --- | --- |
|
![before](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/eng-4412-cimd-rejected/before-notion-invalid-client-id.png)
|
![after](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/eng-4412-cimd-rejected/after-cimd-rejected.jpg)
|

## How Has This Been Tested?

Unit tests cover a rejected (400), accepted (302) and unreachable check
in both paths, and the grouped-error passthrough. A Jest test covers the
button. Live: a local API with `WEB_DOMAIN=https://st-dev.onyx.app`, in
Chrome. Editing a Notion server and adding Linear through Add MCP both
showed the new error. The button turned the switch off, and Connect then
redirected to the provider with a DCR client. Measured `/authorize` for
an unreachable CIMD id: Notion 400, Linear 400, Sentry and Canva 500.
Not yet tested on a deployed st-dev.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.
2026-10-22 00:58:33 +00:00
Wenxi 29bc370294 fix(plugins): restore token entry after MCP URL changes (#16458)
## Description

Restore API-token entry after an administrator changes an MCP server
URL. Saving the new URL clears old header values before opening
Authentication. The retained empty header previously prevented the form
from creating its token field.

Restore the default `Authorization: Bearer {api_key}` template when all
API-token header values are blank. Administrators can then enter a
replacement personal or shared token. Existing nonempty custom headers
remain intact.

This follow-up targets main after the MCP setup stack merged. Its
release backport requires the MCP backport stack ending at #16455.

## How Has This Been Tested?

- 102 MCP authentication, configuration, and detail component tests
passed.
- Regression cases cover replacement personal and shared tokens, the new
server URL, and the submitted authentication template.
- Changed-file pre-commit checks passed, including TypeScript typing and
coverage, lint, formatting, and feature-map integrity.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes the MCP API-token field disappearing after an administrator
changes a server URL. Saving the new URL clears old header values, and a
fully blank header previously prevented the form from creating its token
field. The form now restores the default `Authorization: Bearer
{api_key}` template whenever all API-token header values are blank, so
administrators can enter a replacement personal or shared token.
Existing nonempty custom headers remain intact.

<sup>Written for commit d1d1caae84.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16458?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/16458?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16458&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

* **Bug Fixes**
* Restored the API-token authentication template when authentication
headers are empty or cleared after changing a server URL.
* Kept the replacement token field available for both user and
administrator authentication flows.
* Preserved the replacement server URL and generated the appropriate
credential payload during submission.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 23:54:33 +00:00
roshan bf62f4bf65 feat(admin): add opt-in Decision Models configuration (#16276)
## Summary

Add Admin > Decision Models for TypeSafe AI, OpenAI, and OpenRouter.
Decision-model selection starts unset and never falls back to the chat
default.

Decision providers have dedicated save and credential-test endpoints.
Their request accepts connection fields and explicit
`selected_model_names`. It rejects chat settings. The database operation
changes decision selections while preserving shared chat settings,
visibility, defaults, and access.

The dedicated decision form separates catalog capability from saved
selection. Catalog refresh and auto-mode synchronization do not select
additional decision models. Clear the selected workspace decision model
before removing its model or provider.

Remove decision-scope flags and branches from the shared chat provider
forms and updates. Shared model settings remain on Language Models. No
classification or review workflow ships in this stack.

## Validation

- 247 related backend unit tests passed, including stored-capability
preservation.
- 15 tests against isolated PostgreSQL passed.
- 24 frontend component tests and full web type checks passed.
- Live requests through the frontend proxy verified selection changes,
chat preservation, and rejection of chat settings.
- All applicable pre-commit hooks passed, including Python and
TypeScript types, Ruff, oxlint, oxfmt, secret scanning, and feature-map
integrity.
- The standard local Playwright setup requires paid group APIs
unavailable on this local deployment. The browser test did not reach its
assertions.

## Stack testing (`29eec61fb4`)

Tested the full stack at the top commit on a local dev stack, desktop
and 390×844 mobile. OpenAI decisions and chat used a real key. TypeSafe,
Zoom, OpenRouter, and Vercel AI Gateway used local mock upstreams; Onyx
auth, validation, persistence, encryption, and UI were real.

- Passed: decision selection starts **Not configured**, independent of
the chat default.
- Passed: live OpenAI decision validation saved `gpt-6-luna`. OpenRouter
discovery and validation worked; bad keys fail visibly.
- Passed: decision saves keep shared chat settings, visibility,
defaults, and access (DB before/after compared).
- Passed: save/test endpoints reject chat settings (422). Catalog
refresh and chat auto-mode do not add decision selections.
- Passed: model and provider removal are blocked until the workspace
selection is cleared.
- Passed (regression): live GPT-5 Mini chat works; decision-only models
are absent from the chat picker.
- **Fixed (`37c248ec04`):** two UI bugs found here. See *Fix retest*
below.

![Decision Models desktop and
mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS82ZTdiZjg3My1kNWYwLTQyOTMtYjk0ZS03NWM1OTFiMDdhMTYiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJkZWNpc2lvbi1tb2RlbHMtZGVza3RvcC1tb2JpbGUucG5nIn0.y0LxVR9yzp-2FviQg6sUUSc79oyekW7PhO9jd1sA2U0)

![OpenAI decision
provider](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9lYmNlMWNiMC1lZmVkLTQyNDQtYWU5Zi1hNjQ0NWM4MDAzYjciLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJvcGVuYWktZGVza3RvcC1tb2JpbGUucG5nIn0.viITouz1WwL-KPGjLAOOxhO2O8OH78a0-f9Ww6IhJHg)

![OpenRouter decision
provider](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8wMTQzMzRlYS1iNjE0LTQ1ZjktOGM3MC1hMWE5ZDAyMWU2NWUiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJvcGVucm91dGVyLWRlc2t0b3AtbW9iaWxlLnBuZyJ9.-k_vhxArLC0sn-efnND-4PrdBaCXCbvXIyODhgYM2O0)

![Live OpenAI decision
flow](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS81NjEyY2VlZC0zMDEyLTRhNDUtYjhiZi1jMWUwNTVjN2EwNDgiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJvcGVuYWktZGVjaXNpb24tZmxvdy53ZWJwIn0.wnYGxw94Kj9NClKTgrANKSLib0ToOs1eROCqesZAdfs)

![Removal
guard](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS82NGUxM2JjOC04YjMwLTRlNmEtYjNhYy00ZDcxOWIxZDMyYTIiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJkZXNrdG9wLWRlbGV0ZS1ndWFyZC5wbmcifQ.QD54xAub9H33NzJCgbJhnJkKI36XdfjvRPFRyu4VUqo)

![Live chat
regression](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9lODJlNGM5Zi0xZjBiLTQxYzAtYTNjZS00M2E0YTFhNmU4ZjMiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJyZWdyZXNzaW9uLWxpdmUtb3BlbmFpLWNoYXQucG5nIn0._YvK1bOI35c1BUkJ8Ubdlzxyx2K7FQs_CfPhqWs44Vo)

## Fix retest (`78cf4ccb12`)

`37c248ec04` fixes two bugs found by the stack test:
- Decision Models showed a deleted provider (e.g. Zoom removed on Voice)
as Configure/Delete until a full reload. `useAdminLanguageModels` now
takes `{ revalidateIfStale }`; Decision Models passes `true`, so the
cached provider list refetches on mount.
- Configure headings rendered literal asterisks (`Configure *Vercel AI
Gateway*`). `DecisionProviderModal` now wraps the title in `markdown()`,
like the shared LLM modal.

Retested in the UI, no page reloads:
- Passed: Zoom connected on Voice and Decision Models, selection
cleared, Zoom disconnected on Voice. Sidebar navigation to Decision
Models showed **Connect**.
- Passed: a Gateway connection made on Language Models showed
**Configure** on Decision Models.
- Passed: Gateway and OpenAI Configure headings show no asterisks.
- Passed: both pages load, and display-name edits save and persist.
- Jest: new regression test for a stale cached provider list, and a
heading assertion.

![Zoom card before and after Voice
disconnect](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS82MzUxZWYwMi0zN2Q3LTRiYWItOWE5My01NWM1NTcyMDQ2ZDYiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJ6b29tLWNhY2hlLWJlZm9yZS1hZnRlci5wbmcifQ.WFkOy2ZxZz4NccAYlxpAcloWWl2DQx-GtsyP2by5PFw)

![Cache navigation
recording](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9hN2QwMzhjMS04YzQwLTQ1ZWQtYjZhOC1mYzViNjM1ODc1NjQiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJjYWNoZS1uYXZpZ2F0aW9uLndlYnAifQ.PKq-o2BIeHa7Y9wjSDZ3BkNdMKHR0kK_EtNztu_8s0M)

![Configure
headings](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS81NTU2NzMzZC02NjBiLTQ4YjItOTdmOC05MzFmYWFiMWU0ODMiLCJpYXQiOjE3OTI2MTIwNTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJjb25maWd1cmUtaGVhZGluZ3MucG5nIn0.8UqmY8ZjvoxXFml-rLNrfj1lCrqb_1MpRql8Zvyb2WQ)

## Stack

PR 2 of 4: #16262 → **#16276** → #16280 → #16284.

Link to Devin session:
https://app.devin.ai/sessions/39d77bde91c34b75922bb05c02b93b90
Open in Devin Desktop:
https://app.devin.ai/desktop/session/39d77bde91c34b75922bb05c02b93b90?variant=devin
Requested by: @rohoswagger
2026-10-21 23:51:27 +00:00
roshan cc110e2bbf feat(llm): add TypeSafe AI provider setup (#16262)
## Summary

Add TypeSafe AI as a model provider so admins can save System One
credentials before decision-model support lands.

- Add the real TypeSafe AI logo, provider card, and credential form in
Language Models.
- Test credentials through TypeSafe's native System One endpoint and
reuse encrypted provider storage.
- Support creation, editing, and deletion without creating chat models
or setting a chat default.
- Keep TypeSafe out of chat onboarding. Reject tests that send saved
credentials to another provider.
- Keep the setup form focused on credentials. Remove the roadmap and
internal validation paragraph from all nine locales.

This PR covers provider setup only. #16276 moves TypeSafe to the
dedicated Decision Models page and adds discovery and optional
selection. No review or classification workflow ships in this stack.

## Validation

- Previously created and edited the provider in the live admin UI with a
real TypeSafe key. Verified saved credentials against System One.
- Confirmed that the provider stores no chat models, stays out of chat
listings, and can be deleted through the API.
- 232 backend unit tests, 25 PostgreSQL tests, and 12 frontend tests
passed.
- Touched-file hooks passed for typing, lint, formatting, translations,
secrets, and feature-map checks.
- After the copy removal, ten setup and locale tests passed on this PR.
Thirteen passed on the full stack. Typecheck and lint passed.

## Screenshots

Current provider-only setup at `475ae5d`, with the real TypeSafe AI logo
and no implementation-status paragraph. Credentials are empty. This
screenshot is separate from the earlier real-key validation.

![TypeSafe AI System One provider
setup](https://github.com/user-attachments/assets/6142bd45-e75c-4621-b154-b75219085f2e)

## Stack testing (`29eec61fb4`)

Tested the full stack at the top commit on a local dev stack, desktop
and 390×844 mobile. OpenAI decisions and chat used a real key. TypeSafe,
Zoom, OpenRouter, and Vercel AI Gateway used local mock upstreams; Onyx
auth, validation, persistence, encryption, and UI were real.

- Passed: bad credentials show an error; good credentials reach
`/v1/systemone`.
- Passed: create, rename, refresh, model removal, and delete.
- Passed: TypeSafe stores decision models only, never becomes the chat
default, and is absent from the chat picker.
- Passed: both credential-test endpoints reject reuse of saved TypeSafe
credentials against another provider; no request reached the other base.

![TypeSafe desktop and
mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9jMjZjOGZjNy1jOWI4LTQzYTktYmMxMi04MGNiODQ4YTZiNDYiLCJpYXQiOjE3OTI2MTA4NjQsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJ0eXBlc2FmZS1kZXNrdG9wLW1vYmlsZS5wbmcifQ.r4M2e6HMLKPhrmT4SfbRcrlBWycQgmKQXvIrLmBv6mE)

![TypeSafe
flow](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8yYzZkM2Q0Yi04ZDcxLTQyMmYtOTYwMi01MTAzNTE4ZWYwZDMiLCJpYXQiOjE3OTI2MTA4NjQsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJ0eXBlc2FmZS1mbG93LndlYnAifQ.N-88hUq9h6n3N_y9OQADTEaXe93FRGOz7EhtVxDY1kI)

![Chat picker excludes
TypeSafe](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9mZGQwNDE5Yy03YTAwLTQyZmYtYjAwOC03Y2RhMGIxM2U3NGYiLCJpYXQiOjE3OTI2MTA4NjQsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJyZWdyZXNzaW9uLXBpY2tlci1uby10eXBlc2FmZS5wbmcifQ.9-8x1pyxpCssI3uPfpOr0rBGVtbiX1FAOmlfyyqdBdM)

## Stack

PR 1 of 4: **#16262** → #16276 → #16280 → #16284.

Link to Devin session:
https://app.devin.ai/sessions/39d77bde91c34b75922bb05c02b93b90
Open in Devin Desktop:
https://app.devin.ai/desktop/session/39d77bde91c34b75922bb05c02b93b90?variant=devin
Requested by: @rohoswagger
2026-10-21 23:51:26 +00:00
devin-ai-integration[bot] 054f786794 chore(mcp): remove helpers and strings left unused by staged setup (#16444)
## Description

Remove code that the staged MCP setup stack (#16419, #16413) left with
no production callers:

- `mcpOAuthReturnHref` in `web/src/lib/plugins/routes.ts`. Its only
caller, the old admin OAuth start in `MCPAuthenticationPage.tsx`, was
removed.
- `consumeMcpChatEnableIntent` in
`web/src/sections/actions/mcpChatEnableIntent.ts`. Its only caller, the
`onTriggerFetchTools` handler in `MCPPluginSetupModal.tsx`, was removed.
The private `checkIntent` helper is now inlined into
`verifyMcpChatEnableIntent`. Behavior does not change.
- The `actions.mcpAuthModal.errors.oauthStage` and `discoveryStage`
strings in all 9 locale files. They were read only through ``
t(`mcpAuthModal.errors.${connectionError.stage}`) ``, and that call was
removed.

Tests that used `consumeMcpChatEnableIntent` to check stored intent
state now use `verifyMcpChatEnableIntent`.

## How Has This Been Tested?

- `jest` for `mcpChatEnableIntent`, plugin routes,
`MCPServerDetailPage`, `sections/actions/modals`, and `src/i18n`: 214
tests passed.
- `bun run types:check`, oxlint, oxfmt, and pre-commit hooks passed.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.

Link to Devin session:
https://app.devin.ai/sessions/55fba60b72e94e1282b8d8caad16b371
Open in Devin Desktop:
https://app.devin.ai/desktop/session/55fba60b72e94e1282b8d8caad16b371?variant=devin

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Removes MCP helpers and locale strings left unused by the staged MCP
setup stack.

- `consumeMcpChatEnableIntent` becomes `verifyMcpChatEnableIntent`,
which no longer deletes the stored proof after checking; callers clear
it explicitly with `clearMcpChatEnableIntent`.
- `mcpOAuthReturnHref` and the `oauthStage`/`discoveryStage` strings in
all nine locale files had no remaining callers and are removed.
- Tests that relied on consuming to expire proof now verify and clear
separately.

<sup>Written for commit 80a68d8574.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16444?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/16444?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16444&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

* **Changes**
* MCP chat authorization checks can now be performed repeatedly during
an authentication flow and remain valid until the flow is cancelled.
  * Cancelled or invalid authorization attempts continue to be rejected.
* Removed obsolete MCP authentication error messages from supported
language translations.
* Removed the previous OAuth return-route behavior for automatically
triggering plugin fetches and enabling chat.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-21 23:46:24 +00:00
devin-ai-integration[bot] ad4534cffc chore(craft): remove unused viewActions translation key (#16433)
## Description

Remove `craft.actionPolicies.viewActions` from all nine message
catalogs. #16408 removed its only caller, `t(onChange ? "manageActions"
: "viewActions")` in `TaskActionPolicies.tsx`. No other code uses the
key, and no code builds `actionPolicies` keys dynamically.

## How Has This Been Tested?

- `bunx jest src/i18n/__tests__` (9 catalog tests) passes.
- Pre-commit passes: TypeScript type check, translation updates,
feature-map integrity.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.

Link to Devin session:
https://app.devin.ai/sessions/70f47988b6014b07bdc5b1a3112017fc
Open in Devin Desktop:
https://app.devin.ai/desktop/session/70f47988b6014b07bdc5b1a3112017fc?variant=devin

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Removes the unused `craft.actionPolicies.viewActions` translation key
from all nine message catalogs. #16408 removed its only caller, so the
key is no longer referenced by any code.

<sup>Written for commit 4e1c613aae.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16433?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/16433?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16433&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

* **Localization**
* Removed the “View actions” translation entry from supported language
resources.
* The corresponding action-policy label may no longer appear in
localized interfaces.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-21 23:31:57 +00:00
Wenxi 06b6fa03fb fix(plugins): open tools after MCP setup completes (#16413)
## Description

Open the saved plugin's Tools tab after MCP setup succeeds, including
OAuth connections with an existing valid grant. Setup previously
returned to Configuration, which made successful authentication appear
to restart setup.

Separate authentication error notices from their recovery buttons with
consistent spacing. This includes the Use your own OAuth client action
after automatic registration fails.

This PR builds on #16419. It uses the existing detail route with
`tab=tools` and preserves the stack's staged validation and name-first
focus. The stack includes the shared Chat update fix from main. The
temporary exclusive scheduling workaround is removed.

## How Has This Been Tested?

- Passed all 174 tests across the affected UI, MCP service, and
translation suites after rebasing.
- Verified completed setup selects Tools without repeating discovery.
Legacy OAuth return coverage still selects Tools and discovers once.
- Passed changed-file checks for TypeScript, coverage, formatting, lint,
secrets, and feature-map integrity.
- Playwright CI will rerun on this stacked revision, including the
personal-key tests under normal scheduling.
- The registration-error recovery test passes after the spacing fix.
- Live external provider consent was not tested.

## 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**
* MCP setup completion now opens the plugin detail page directly on the
Tools tab.
* OAuth returns and existing authenticated connections open the Tools
tab, with legacy OAuth returns continuing to support tool discovery.
* Direct navigation to the Tools tab is supported without triggering
tool discovery.

* **Bug Fixes**
* Improved the MCP authentication error layout while preserving recovery
options for registration and discovery failures.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 23:21:14 +00:00
Wenxi d942a6d457 fix(plugins): keep MCP edits unsaved until connection succeeds (#16419)
## 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 -->
2026-10-21 23:21:14 +00:00
Wenxi 381123cae9 fix(plugins): improve MCP setup and configuration (#16414)
## Description

MCP setup focuses Server Name when opened and when returning from
Authentication.

MCP setup shows neutral indicators until a valid server URL starts
discovery. Spinners begin after the existing 700-millisecond debounce.
Clearing or changing the URL resets both indicators and cancels the
previous probe.

OAuth Pass-through appears in the authentication dropdown when Onyx
supports OAuth login, even with advanced settings collapsed. It remains
an explicit choice.

Configuration now shows the server URL. Users with edit permission can
change it; other viewers see a read-only field. Saving a new URL uses
the existing backend credential reset and opens Authentication to
reconnect and discover tools. The saved response replaces cached server
data before reloading. Whitespace-only edits preserve the existing
connection, and metadata-only saves omit the URL.

The preview also exposed missing client boundaries in Opal. The
date-range picker and toast hooks now declare their client boundary at
their source modules.

## How Has This Been Tested?

- Setup focus regression: 40 component tests passed; signed-in browser
confirmed Server Name receives initial focus.
- MCP setup and authentication component suites: 79 tests pass.
- MCP configuration and detail component suites: 51 tests pass,
including invalid URLs, edit permissions, unchanged URL preservation,
and a failed reload after saving a new URL.
- Date-range picker component suite: 6 tests pass.
- Changed-file pre-commit checks pass, including TypeScript and type
coverage, formatting, lint, and feature-map integrity.
- Live signed-in preview: Configuration shows the saved URL and permits
admin edits, with no browser page errors. No live server URL was
changed.
- Live preview: `/app` and `/admin/plugins` render without page errors.
Blank MCP setup shows both discovery stages idle.

## 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**
* MCP server URLs can be edited with required HTTP(S) validation and
remain read-only for viewers.
* URL changes trigger authentication, reconnection, and tool rediscovery
as needed.
  * OAuth pass-through is available whenever OAuth login is enabled.
* Probe stages clearly show idle, pending, successful, and failed
states.
* Direct tab navigation and improved focus handling are supported in
server setup and details.

* **Bug Fixes**
* Clearing or changing a URL resets probe status and cancels outdated
checks.
  * Metadata-only saves preserve the existing endpoint.
  * Updated server details remain visible if reloading fails.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 23:21:11 +00:00
roshan 1af99d61cb chore(craft): improve Kubernetes startup, image cleanup, and local debugging (#16376)
## Description

Craft development required repeated image builds, copied cluster
settings, and editor-specific Telepresence commands. Startup could
report success before services were usable.

- Share configuration resolution and launch commands between Make and VS
Code. Read deployed ConfigMaps and Secrets in memory, with personal
`.env.k8s` overrides.
- Add separate infrastructure and application readiness checks,
`craft-doctor`, and shared Telepresence connection/intercept handling.
- Reuse images, detect source changes, and provide checkout-scoped
cleanup previews. Add pause/resume and migration commands with
database-target checks.
- Default Kubernetes sandbox callbacks to the internal API Service.
Preserve explicit `ONYX_SERVER_URL` overrides and bump the Helm chart to
0.9.20.
- Update the local Kubernetes guide, Craft development skill, and
feature map.
- Give Helm chart CI a 100 GB runner disk. The previous runner exhausted
disk while unpacking OpenSearch.
- Upgrade the Storybook package family to 10.6.2 to fix the critical
audit finding GHSA-f2ph-cmg5-wxc8.

Existing `.env.k8s` files remain untouched; old service entries continue
to override chart settings. Image refresh updates the PodTemplate for
future sandboxes; existing sandbox pods retain their images.

## Review guide

[Read or download the Markdown review
guide](https://github.com/onyx-dot-app/onyx/blob/d3cbc42d89bfb150d1cfc815638645043c34b69e/docs/craft/dev/kubernetes-review-guide.md).

## How Has This Been Tested?

- Live kind and Telepresence: startup, intercept reuse, doctor, API and
worker hot reload, readiness success/failure, and migration
status/upgrade.
- Three real Craft smoke tests: sandbox provisioning, file
upload/readback, and web app preview through the frontend. No real LLM
calls were exercised.
- 76 Craft Helm tests and 27 development-tool regression tests passed.
- Pre-commit checks, explicit development-tool type checking, frontend
type coverage, and skill validation passed.
- The local web audit passes its critical-severity gate without
suppressions. The Storybook production build passes.
- Removed the disposable test cluster, local processes, and temporary
files.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Harden local Craft Kubernetes development by sharing configuration and
launch logic between Make and VS Code, and by checking service readiness
before startup reports success.

**What’s included**
- Resolve deployed ConfigMaps, Secrets, and container env in memory
through a shared launcher; personal `.env.k8s` values take precedence,
and resolved secrets never touch disk or terminal output.
- Add shared lifecycle commands (`craft-doctor`, staged
infrastructure/application readiness, Telepresence connect and
intercept, pause, resume, migration, cleanup previews) used by both Make
and VS Code.
- Reuse built images when source is unchanged, force a rebuild with
`--rebuild-sandbox-image`, and keep probe and command invocations
bounded while preserving actionable errors.
- Default sandbox callbacks to the internal API Service, preserving
explicit `ONYX_SERVER_URL` overrides; drop the now-unneeded validation
and bump the Helm chart to 0.9.20.
- Give the Helm CI runner a 100 GB disk, since Docker and kind each
store images and OpenSearch exhausted the default disk. Bump Storybook
tooling to 10.6.2 to patch a development server vulnerability.
- Update the local Kubernetes guide, Craft development skill, feature
map, and add a downloadable PR review guide.

**Validation**
- Passed 76 Craft Helm tests, 27 development-tool regression tests, and
new unit tests for the shared runtime and lifecycle CLI.
- Tested startup, readiness, Telepresence, hot reload, migrations, and
three Craft smoke tests against kind; no real LLM calls were exercised.

<sup>Written for commit d3cbc42d89.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16376?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/16376?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16376&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 a unified local Kubernetes workflow for Craft, including API and
worker launching, connectivity, readiness checks, migrations,
pause/resume, diagnostics, cleanup, and disk management.
- Added sandbox image reuse, rebuilding, loading, and configurable image
selection.
- Sandbox callbacks now default to the internal API service URL, with
support for explicit overrides.

- **Documentation**
- Added guidance for local Kubernetes setup, configuration, lifecycle
management, troubleshooting, and verification.

- **Bug Fixes**
- Improved readiness validation and stopped sensitive generated
credentials from being printed.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 23:15:16 +00:00
Justin Tahara f6249c71fa feat(llm): assume an AWS role for cross-account Bedrock (#16357)
## Description

Adds cross-account Bedrock. Onyx can run in one AWS account and call
Bedrock in another. The Bedrock provider gets two optional fields:
**Role ARN to assume** and **External ID**.

- **Chat calls:** `AWS_ROLE_NAME` / `AWS_EXTERNAL_ID` in `custom_config`
map to LiteLLM's `aws_role_name` / `aws_external_id`. LiteLLM 1.93 does
the STS AssumeRole, including from IRSA, and caches the credentials.
- **Model fetch:** `/admin/llm/bedrock/available-models` assumes the
role with the submitted credentials (IAM or access key), then lists
models with the temporary credentials.
- **Admin modal:** the two fields show for the IAM and access key auth
methods. They are cleared for the long-term API key method, because a
bearer token is bound to one account.
- **Cloud:** multi-tenant deployments reject these keys on provider
upsert, on provider test, and on model fetch. The pods' shared identity
would let any tenant assume any role that trusts it. The modal still
shows the fields on cloud; the backend rejects them (the same pattern as
Vertex Workload Identity).

The env var fallback (`AWS_ROLE_NAME` on the pods) works today without
this change. This PR makes the setting per-provider and fixes the model
fetch, which ignored the role.

## Screenshots

**Access key + role.** The role fields show below the credentials. Both
are optional.

<img width="600" alt="Access key auth with role ARN and external ID"
src="https://github.com/user-attachments/assets/1d934b1a-17ac-4524-8449-40091ffbc5f3"
/>

**Environment IAM role + role.** The pod's own role (for example IRSA)
assumes the role in the other account.

<img width="600" alt="IAM auth with role ARN and external ID"
src="https://github.com/user-attachments/assets/8631c344-4c46-4367-a658-16e92490367d"
/>

**Long-term API key.** The role fields are hidden and cleared, because a
bearer token works in only one account.

<img width="600" alt="Long-term API key auth hides the role fields"
src="https://github.com/user-attachments/assets/10d1ae9c-3ebb-4c1a-9600-e399b42e5ab1"
/>

**Model fetch with a role.** With dummy keys, the error comes from the
`AssumeRole` call. This shows the fetch assumes the role before it lists
models.

<img width="800" alt="Model fetch fails at AssumeRole with dummy keys"
src="https://github.com/user-attachments/assets/66f9c9e1-9c63-4136-afc3-73e08a29447b"
/>

## How Has This Been Tested?

- Unit: the role keys map to LiteLLM kwargs; the model fetch calls
`sts.assume_role` with the base credentials and lists models with the
temporary credentials; multi-tenant rejection in the model fetch and in
the write-path gate.
- `tsc`, `oxlint`, `oxfmt` run by hand (no `bun` locally); CI runs the
real hooks.
- Local run of this branch (fresh DB): the modal shows and clears the
fields for each auth method; the model fetch returns `400
CREDENTIAL_INVALID` from `AssumeRole` with dummy keys.
- Not yet tested against two real AWS accounts.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds cross-account Bedrock support so Onyx can run in one AWS account
and call Bedrock in another, with the Bedrock provider assuming a role.

- Adds optional Role ARN and External ID fields to the Bedrock provider.
- Chat calls map the fields to LiteLLM's `aws_role_name` and
`aws_external_id`, which performs the STS AssumeRole and caches
credentials.
- Only the uppercase `AWS_ROLE_NAME`/`AWS_EXTERNAL_ID` spellings are
accepted; lowercase spellings are dropped when an admin edits the
provider.
- `/admin/llm/bedrock/available-models` now assumes the role with the
submitted credentials before listing models; previously it ignored the
role.
- The admin modal shows the two fields for the IAM and access key auth
methods, and clears them when the long-term API key method is selected,
since a bearer token is bound to one account.

**Security**
- Multi-tenant deployments reject these keys on provider upsert,
provider test, and model fetch, because the shared server identity would
let any tenant assume roles that trust it.

<sup>Written for commit 468233d940.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16357?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/16357?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16357&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**
- Amazon Bedrock configurations now support cross-account IAM role
assumption with an optional external ID.
- Administrators can enter these settings in the language-model setup
and use them when discovering available models.
- Saved role settings are displayed for existing configurations and
cleared when bearer-token authentication is selected.
- Role assumption is unavailable in multi-tenant deployments and
unsupported for bearer-token authentication.

- **Documentation**
- Added setup guidance and localized interface text for the new Bedrock
options.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 22:41:00 +00:00
Yuhong SunandClaude Opus 5.5 b86caa7332 feat(indexing): add new embedding models and upgrade-only model selection (#15351)
## Description

Add 11 selectable embedding models and an upgrade-only model picker.
Fresh self-hosted installs use Granite. Existing deployments keep their
stored model until an admin starts a re-index. On startup, the API
server waits until the model server can embed with the self-hosted model
in use. If that model is not in its cache, the model server downloads it
first.

| Type | Model | Output dimensions |
| --- | --- | --- |
| Self-hosted | `ibm-granite/granite-embedding-97m-multilingual-r2`
(default, in the image) | 384 |
| Self-hosted | `voyageai/voyage-4-nano` | 2048 |
| Self-hosted | `nvidia/Nemotron-3-Embed-1B-BF16` | 2048 |
| Cohere | `embed-v5.0-pro`, `embed-v5.0-fast` | 2048 |
| Google | `gemini-embedding-2` | 3072 |
| OpenAI | `text-embedding-3-large`, `text-embedding-3-small` | 3072 /
1536 |
| Voyage | `voyage-4-large`, `voyage-4`, `voyage-4-lite` (added on main)
| 1024 |

The 16 older models are now **legacy**: nomic, E5, GTE-small, Cohere
v3/v4, Voyage v2 and older Gemini/Vertex models. Existing indexes on
them keep working. The API refuses a legacy model as a new target, but a
re-index with the current model is still allowed.

## Size of this PR

After review, I removed the parts that the feature does not need. The
behavior stays the same. The diff against `main` went from **80 files,
+9,855 / −504** to **69 files, +4,740 / −467**.

| Area | Files | Added | Removed |
| --- | --- | --- | --- |
| Backend source | 16 | 1,004 | 96 |
| Backend unit tests and test managers | 16 | 1,568 | 32 |
| Backend manual suites (opt-in, not in CI) | 7 | 617 | 0 |
| Web source | 6 | 617 | 255 |
| Web tests | 5 | 630 | 1 |
| Web messages (9 locales) | 9 | 225 | 72 |
| Deployment, CI and feature map | 10 | 79 | 11 |

The feature map (`.agents/feature-map/`) now describes the new selection
rules.

Removed:

- **The branch's own cloud probe.** All selectable cloud models now use
main's probe (`probe_embedding_dimension`), which Bifrost already used.
It now also sends the stored Vertex configuration, the Azure API version
and the deployment name. Before this change, the branch's probe ignored
the Vertex configuration, so a Google provider that uses Workload
Identity could not switch to `gemini-embedding-2`.
- **The Vertex fan-out.** Main already sends `gemini-embedding-2` texts
one at a time. The Vertex code is now the same as on main.
- **The `expected_dim` path** in the model-server loader, and the
startup warning for custom models with registry names. These protected
only one rare case; see **Custom models with a registry name** in [Known
limits](#known-limits).
- **The UI fallback** that tested a new key with a default model when
the key failed with the current model. The credential test now embeds
with the staged or current model of that provider. Only a provider with
neither uses its first selectable model.
- **Picker cards for current and staged bring-your-own models.** Main
does not show them either. The current model shows in the summary at the
top of the page.
- **Shared gates and fixtures for the manual suites.** Each suite has
one gate env var.
- **The screenshots in `docs/assets`.** The images below come from an
earlier commit of this PR.
- **Tests for removed code, and repeated test cases.** The unit tests
went from 3,842 to 2,667 lines.

### CPU speed and memory cards

All nine locales show these planning estimates:

| Model | Card speed | Recommended CPU RAM |
| --- | --- | --- |
| Granite 97M | Up to **200 docs / min / CPU core** | **4 GB** per model
server |
| Voyage 4 Nano | Up to **50 docs / min / CPU core** | **4 GB** per
model server |
| Nemotron 3 Embed 1B BF16 | Up to **5 docs / min / CPU core** | **8
GB** per model server |

Nemotron says **GPU recommended**. It runs on CPU too, but slowly. The
cards tell the admin that speed changes with the hardware and the
document size. The RAM value is for one loaded model. A re-index and the
rest of Onyx need more memory.

These values are planning estimates, not guarantees. The Nemotron
per-core value is an extrapolation from an earlier multi-thread run.
Document counts depend on chunking, and throughput does not always scale
linearly with cores. The RAM values are recommendations, not measured
minimum or peak values.

## Implementation

- **Registry.** `shared_configs/embedding_models.py` defines the model
names, dimensions, prefixes, normalization, status and pinned Hugging
Face revisions. A parity test compares it with the web registry.
- **Upgrade-only guard.** `_check_embedding_model_target` runs in
`set-new-search-settings`:
- The current model can always re-index, legacy models too. The
dimension must stay the same. In Onyx Cloud, `reduced_dimension` must
also stay the same, because tenants share the index of a model.
- A selectable model must use the registry settings (dimension,
normalization, prefixes).
- Other OpenAI, Cohere, Google and Voyage models, and legacy self-hosted
models, get `INVALID_INPUT`. The error lists the selectable models.
  - Custom Hugging Face models, LiteLLM, Azure and Bifrost stay open.
- A self-hosted model gets `INVALID_INPUT` for a new
`reduced_dimension`, because the model server ignores it. A re-index
keeps a stored value.
- **Cloud probe.** Before a re-index to a selectable cloud model, the
API embeds a test text with the stored credentials and checks the
dimension. A bad key, region or model ID fails before indexing starts.
The probe runs before the PRESENT row lock, with no open transaction, as
main's Bifrost probe does.
- **Self-hosted loads.** New models load at a pinned revision with
`trust_remote_code=False`, a dimension check and a finite-vector check.
CPU-only hosts load FP32. CUDA and MPS keep the checkpoint dtype. Legacy
and custom models keep main's `SentenceTransformer` call, so their
vectors do not change.
- **Voyage 4 Nano.** An Onyx loader adds bidirectional attention and the
learned 1024-to-2048 projection. The generic loader with remote code
disabled does not apply them.
- **Model server.** All models load from the cache first, then from
Hugging Face. Loads run off the event loop on two load workers, so at
most two sets of weights (PRESENT and FUTURE) load at the same time.
Registry models get 32 tokens of headroom for the prefix. The RoPE
pre-warm now runs again only for a longer context. On main, it ran for
every change of context length.
- **Image.** The model-server image bundles pinned Granite. It no longer
bundles nomic: nomic downloads on first use. The startup cache merge
keeps the models that a volume already has.
- **Picker.** Self-hosted and cloud groups, a Legacy label on the
current legacy model, and an **Advanced** section for custom Hugging
Face models, LiteLLM, Azure and Bifrost. A staged custom model keeps the
custom provider, also when its name is the same as a registry model.
- **Error detail.** `EmbeddingModel` now keeps the model server's error
detail. Before, every HTTP failure showed "HTTP error occurred -
response is None".

### Startup wait for self-hosted models

On startup, the API server waits until the inference model server can
embed with the current self-hosted model. The model server loads the
model on the first request, and downloads it first if it is not cached.
Example: a deployment that upgrades while it uses nomic, which the new
image does not bundle.

- The API server logs the wait, each failed attempt with its cause, and
the time when the model is ready. The model server logs a download.
- `LOCAL_EMBEDDING_MODEL_STARTUP_TIMEOUT` sets the wait (default 1800
seconds). When the wait runs out, startup fails. The error names the
model server, the model, the last error and the fix. `0` disables the
wait.
- The check request runs in a daemon thread. A request that still waits
at the deadline (for example, during a slow download) therefore does not
keep the failed process alive. Before this fix, the process stayed alive
until the model server read timeout (600 s by default).
- There is no wait in Onyx Lite (`DISABLE_VECTOR_DB`), for cloud models,
in multi-tenant deployments, in integration tests, or with
`DISABLE_MODEL_SERVER`.
- The wait uses the existing embed endpoint, so it also works with an
older model server during a rolling upgrade.
- This replaces the startup call to `warm_up_bi_encoder`. `SKIP_WARM_UP`
(default `true`) skipped that call, so it did not run by default.
- Only the inference model server is checked. The indexing model server
still loads a model on first use. In Compose, the API server does not
know the host of the indexing model server.
- `env.template` and Helm `values.yaml` document the setting. The Helm
chart is now 0.9.19.

Live test at commit `0eb5881da9`, with an existing nomic deployment on a
local stack. The size reduction did not change the wait:

| Case | Result |
| --- | --- |
| nomic not cached, model server online | The model server downloaded
nomic (523 MB). Startup continued after 19 s. |
| nomic cached | Ready in 3 s, with no download. |
| Model server starts after the API server | A warning every 10 s, then
ready at 33 s. |
| nomic not cached, no internet access | Each warning showed the cause
("not in the model server's Hugging Face cache and could not be
downloaded"). Startup failed at the timeout, and the process exited. |
| Onyx Lite, no model server | No wait. |
| Timeout `0` | No wait. The model loads on first use. |

## Upgrade notes

- **Offline deployments that use nomic.** The new image does not bundle
nomic. A deployment with no internet access and no kept model cache
cannot download it. The API server then fails at startup with a clear
error, instead of starting with a search that cannot embed queries.
Before you upgrade such a deployment, add the current model to the model
server's Hugging Face cache, or keep the cache on a volume. Helm has no
cache volume by default. This PR has no schema migration, so a return to
the previous image works while the current model stays the same.
- **Helm.** The chart has no Hugging Face cache volume for the model
servers, so a model that the image does not bundle (nomic, Voyage 4
Nano, Nemotron) downloads again on every pod start. `values.yaml`
documents an opt-in volume with the existing
`inferenceCapability.volumes` and `volumeMounts` values. A default
volume is not added: a ReadWriteOnce claim hangs rolling updates across
nodes, breaks `replicaCount` above 1, and needs a StorageClass. The
bundled Granite works with or without a volume.
- **API probes.** `/health` answers only after the startup wait. A
liveness probe needs a startup probe that allows the migrations plus
`LOCAL_EMBEDDING_MODEL_STARTUP_TIMEOUT`. The api probe example in
`values.yaml` shows one.
- **Multi-tenant template snapshots.** Migration `dbaa756c2ccf` seeds a
search settings row with `DOCUMENT_ENCODER_MODEL`. When that variable is
empty, the seed is now Granite, not nomic. If a deploy runs
`alembic/check_tenant_snapshot.py`, it reports a difference in
`search_settings` until the template snapshots are built again
(`--snapshot-template`). To keep the seed the same as on main, set
`DOCUMENT_ENCODER_MODEL=nomic-ai/nomic-embed-text-v1`.
- **No return to a legacy model.** After a switch, the old index is
cleaned up and the upgrade-only rule blocks a return to a legacy model.
Keep a compatible backup before you switch.

## Audit: retries and failures

| Layer | Behavior | Compared with main |
| --- | --- | --- |
| Local encode | Catches `RuntimeError`, waits 0.1 seconds, then tries
again: **up to four encode attempts**. It also retries errors that do
not go away, such as OOM. | Unchanged. |
| API to model server | Passage requests retry request failures three
times with five-second waits. HTTP 429 allows ten attempts with
ten-second waits. Connect/read timeouts are 30/600 seconds. Query
requests do not get these retries. | Unchanged. |
| Document indexing | A failed batch falls back to one document at a
time after a two-second delay. Failures are recorded, and the other
documents continue. | Unchanged. |
| Cloud embedding | Authentication errors are separate. Errors mapped to
`RuntimeError` get exponential backoff with jitter: eight attempts in
indexing-only mode, else two. | Unchanged. `gemini-embedding-2` sends
one text per request, in sequence, as on main. |
| Model loading | Cache first, then online. A partial cache falls back
online. A failed load is not cached, so a later request can try again.
Requests for the same model and context length share one load job. |
Cache-first lookup is new for all models. Registry models add pinned
revisions and checks. |

Model downloads run off the event loop. When a waiting request is
cancelled, the shared load continues. A load job has no deadline, so it
can continue after an HTTP timeout.

One gap from main remains: the response JSON is parsed outside the retry
wrapper. See [local
retries](https://github.com/onyx-dot-app/onyx/blob/4784da70697fb59261f43088383d85671544ec98/backend/model_server/encoders.py#L157),
[request
retries](https://github.com/onyx-dot-app/onyx/blob/4784da70697fb59261f43088383d85671544ec98/backend/onyx/natural_language_processing/search_nlp_models.py#L1322),
and [cloud
retries](https://github.com/onyx-dot-app/onyx/blob/4784da70697fb59261f43088383d85671544ec98/backend/onyx/natural_language_processing/search_nlp_models.py#L942).

## Audit: migration and cancellation

There is **no schema migration, and no automatic change to existing
embedding settings**. Granite is the fresh-install default. Cloud tenant
provisioning still uses Cohere v3. Existing stored prefixes and
dimensions stay in use.

1. The API checks the target with the upgrade-only guard.
2. A new cloud target embeds a test text with the stored credentials
before the migration. The probe checks the dimension. It runs before the
PRESENT row lock.
3. The API locks PRESENT and refuses overlapping migrations. It also
refuses an index name that is live or still waiting for cleanup. FUTURE,
the first work and the cleanup consent commit together.
4. The existing port flow reads stored documents from the current index,
embeds them again and writes the new index. Connectors do not fetch
again. All-connectors mode includes paused connectors. Active-only mode
leaves them out and needs consent for the data that it leaves out. User
files are included.
5. Normal promotion waits for successful required ports, no open writers
or sync work, and a sample check in the new index. A failed or paused
required port blocks promotion. The sample check does not check the full
corpus.
6. Port batches retry up to five times. The cursor moves after a
successful batch. The first watchdog retry is immediate. Repeated
failures at the same cursor use exponential backoff from 30 seconds, up
to one hour. Five failures in sequence at that cursor pause the unit. An
operator can resume it.
7. A cancel of FUTURE keeps PRESENT active, stops port work at a batch
boundary, and removes the partial index after writers stop. **Instant
switch promotes first**, so search is not complete during backfill, and
this cancel path is not available.

See [migration
endpoint](https://github.com/onyx-dot-app/onyx/blob/4784da70697fb59261f43088383d85671544ec98/backend/onyx/server/manage/search_settings.py#L140),
[upgrade-only
guard](https://github.com/onyx-dot-app/onyx/blob/4784da70697fb59261f43088383d85671544ec98/backend/onyx/server/manage/search_settings.py#L456),
[swap
gates](https://github.com/onyx-dot-app/onyx/blob/4784da70697fb59261f43088383d85671544ec98/backend/onyx/db/swap_index.py#L367),
and
[cancellation](https://github.com/onyx-dot-app/onyx/blob/4784da70697fb59261f43088383d85671544ec98/backend/onyx/server/manage/search_settings.py#L668).

## Known limits

- **Model memory.** Two load workers limit how many models load at the
same time. Loaded models are not evicted (as on main), so the old and
new models stay in memory during a switch. Helm's default memory limit
for the indexing model server is **6 GiB**, below the **8 GB** that the
Nemotron card recommends. See [load
executor](https://github.com/onyx-dot-app/onyx/blob/4784da70697fb59261f43088383d85671544ec98/backend/model_server/encoders.py#L35)
and [Helm
limits](https://github.com/onyx-dot-app/onyx/blob/4784da70697fb59261f43088383d85671544ec98/deployment/helm/charts/onyx/values.yaml#L309).
- **Legacy models are not selectable.** This is by design. The current
legacy model stays visible and can re-index. After a switch, the
standard flow cannot select it again.
- **Bring-your-own options are under Advanced.** Unknown model names for
the built-in OpenAI, Cohere, Google and Voyage providers get
`INVALID_INPUT`. LiteLLM, Azure, Bifrost and custom models stay open.
- **The cloud probe reads the provider settings once.** If an admin
changes the key or region while a re-index starts, the probe can use the
old values. A key can also change at any time during a re-index, so no
preflight check can guarantee the key that the workers use. Indexing
failures then show in the re-index status, and the admin can cancel.
- **Custom models with a registry name.** Before this PR, an admin could
add a registry model name, such as `voyageai/voyage-4-nano`, as a custom
model. The model server now loads that name as the registry model. For
`voyage-4-nano`, the vectors then have 2048 dimensions, not 1024, so a
1024-dimension index fails with an error. Re-index to the registry model
to fix it. For the other registry models, a generic load gives the same
dimension.
- **Shared `max_seq_length`.** The model server changes `max_seq_length`
on the shared model for each request. Two requests with different
context lengths can overlap. This is the same on main. This PR does not
change it.
- **Voyage 4 Nano depends on the Transformers and SentenceTransformers
layout.** Run the real-weight suite when those dependencies change.
- **No reduced dimensions for Cohere v5 and the self-hosted models.**
Cloud provisioning stays on Cohere v3. These are follow-ups.
- **The cancel dialog has two buttons labeled Cancel.** One closes the
dialog, the other cancels the re-index. This is the same on main.
- The branch is rebased onto `main` at `ad9c0030e9`. This PR does not
claim better retrieval quality.

## Validation for this update

- **Backend: 15,343 unit tests passed** before the review fixes. I left
out the EE license tests, because they write a key to a live Redis. `ty`
and pre-commit passed. After the review fixes and the merge of `main`,
the PR's backend unit tests passed again (199 tests). Pre-commit passed
on all 69 changed files.
- **Startup exit check:** a fake model server that never answers, with a
3 s wait and a 20 s read timeout. The wait raised at 3.3 s. The process
exited at 5 s with the fix, and at 22 s without it.
- **Web: type check, lint and 3,236 Jest tests (307 suites) passed.**
- **Self-hosted real-weight suite: 6 of 6 passed.** Golden scores and
the bidirectional-attention check for Granite, Voyage 4 Nano and
Nemotron.
- **Cloud live suite: `gemini-embedding-2` passed.**
- **End to end on a local stack with branch code: 11 of 11 passed.** The
API refused all 6 legacy self-hosted targets and accepted a same-model
re-index. Then the stack switched to Granite, Voyage 4 Nano, Nemotron
and `gemini-embedding-2`, in sequence. After each switch, a pure vector
search found each of 5 seeded facts first.
- **Not run for this update: Cohere, OpenAI and Voyage cloud tests.** A
TLS proxy on the test network blocked `api.cohere.com` and
`api.openai.com`. The probe then refused the switch with `400`, as
designed. These tests passed on an earlier commit of this PR, and the
probe that they use is main's probe. I had no Voyage key.
- **CI:** at `9321cb51db`, every check passed except the Dependency
Audit: Playwright, integration, acceptance, database,
external-dependency unit, Jest and quality checks. The audit fails on
open Dependabot alerts for Pillow and certifi in other manifests
(`loadtest/uv.lock` and the Craft sandbox requirements). Other open PRs
fail the same way at the moment.
- **Not run for this update:** speed benchmarks.

<details>
<summary>Earlier validation in this PR, not run again for this
update</summary>

- 28 real-weight tests and 24 cloud tests passed.
- End-to-end switches to all eight selectable models of that time
passed. The API refused all 16 legacy targets.
- Legacy-upgrade equivalence, Docker cache keeping and multi-tenant
provisioning checks passed.
- 11,521 backend unit tests passed. 10 EE license failures also occurred
on main.

</details>

## Voyage retrieval-quality check — October 14, 2026

The branch's Voyage loader passed a local EnterpriseRAG-Bench loading
check. **Vector-only mean document Recall@10 was 91.85%**, above the
proposed 50% threshold. This supports a correct load. It does not prove
numerical equality with the upstream implementation.

The corpus had **5,000 documents**: all **722 labeled supporting
documents** and **4,278 random distractors**, sampled with seed
`20261013`. All **470 questions with document labels** were scored. The
30 questions with no labels were left out. No question or answer text
went into the corpus.

| Retrieval mode | Mean Recall@1 | Mean Recall@5 | Mean Recall@10 | Mean
Recall@20 |
| --- | --- | --- | --- | --- |
| Onyx content-vector-only retrieval | 66.13% | 87.37% | **91.85%** |
94.16% |
| Onyx hybrid search API | 75.85% | 93.32% | **96.37%** | 97.70% |

Recall is the fraction of labeled documents found for each question,
averaged over the questions. Rankings use the first hit of each
document. Vector-only hit rate@10 (at least one relevant document) was
94.26%. That is a different metric from recall.

| Question category | Questions | Vector-only Recall@10 | Hybrid
Recall@10 |
| --- | --- | --- | --- |
| Basic | 175 | 97.71% | 99.43% |
| Semantic | 125 | 84.80% | 92.80% |
| Intra-document reasoning | 40 | 97.50% | 100.00% |
| Project related | 40 | 86.43% | 95.35% |
| Constrained | 30 | 98.33% | 98.33% |
| Conflicting information | 20 | 90.00% | 97.50% |
| Completeness | 20 | 68.03% | 79.03% |
| Miscellaneous | 20 | 100.00% | 100.00% |

The check used CPU FP32, 2048-dimensional normalized vectors, and the
registry's query and document prefixes. MPS and CUDA were off. There was
no query expansion, reranking, or LLM document selection. All 5,000
document IDs matched the saved manifest, with 15,612 indexed chunks. All
query vectors were finite and had unit length. The last indexing attempt
had no document errors. Two earlier attempts stopped after
stale-heartbeat timeouts and continued from saved progress.

**Search path:** on this OpenSearch branch, `hybrid_alpha=1` still runs
hybrid retrieval, so the API result includes keyword scores. The
vector-only result calls `OpenSearchDocumentIndex.semantic_retrieval`
directly, with content vectors only. Keyword matches therefore cannot
hide a broken embedding loader in that result.

**Scope:** this is an easier 5,000-document subset that contains all
gold documents, not the full 511,958-document benchmark. Do not compare
these values with full-corpus leaderboard scores. No loader or ranking
code changed to get these results.

Revisions: Onyx `913bfa8d7a32c9cda340df92f42d0f706fd424c2`;
EnterpriseRAG-Bench `d36685e273713975ee20299bbf1ab64165575b3c`; Voyage
`67fabc9bef010dabc5f6024aa1b1b6b93410426f`. Runtime: PyTorch 2.9.1,
Transformers 5.14.1, SentenceTransformers 5.4.1.

## UI screenshots

From the local development stack at commit `98bfc70080`, before the size
reduction. These show the real flow, not mockups.

<details open>
<summary>Self-hosted cards and current model</summary>

![Self-hosted model cards with CPU throughput and RAM
guidance](https://raw.githubusercontent.com/onyx-dot-app/onyx/98bfc70080a541f9346517d062f1c9edbdd51ffb/docs/assets/embedding-models/01-model-cards.jpg)

</details>

<details>
<summary>Cloud model catalog</summary>

![Cloud model
catalog](https://raw.githubusercontent.com/onyx-dot-app/onyx/98bfc70080a541f9346517d062f1c9edbdd51ffb/docs/assets/embedding-models/02-cloud-models.jpg)

</details>

<details>
<summary>Staged Granite change and migration strategy</summary>

![Staged model
change](https://raw.githubusercontent.com/onyx-dot-app/onyx/98bfc70080a541f9346517d062f1c9edbdd51ffb/docs/assets/embedding-models/03-staged-change.jpg)

</details>

<details>
<summary>Re-index in progress</summary>

![Re-index progress and cancellation
action](https://raw.githubusercontent.com/onyx-dot-app/onyx/98bfc70080a541f9346517d062f1c9edbdd51ffb/docs/assets/embedding-models/04-reindex-progress.jpg)

</details>

<details>
<summary>Cancel confirmation</summary>

![Cancel re-index
confirmation](https://raw.githubusercontent.com/onyx-dot-app/onyx/98bfc70080a541f9346517d062f1c9edbdd51ffb/docs/assets/embedding-models/05-cancel-confirmation.jpg)

</details>

<details>
<summary>Advanced custom model setup</summary>

![Custom embedding model
setup](https://raw.githubusercontent.com/onyx-dot-app/onyx/98bfc70080a541f9346517d062f1c9edbdd51ffb/docs/assets/embedding-models/06-custom-model-dialog.jpg)

</details>

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.
- [ ] [Optional] Override Linear Check

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:40:21 +00:00
Nikolas Garza 8e7484e462 fix(indexing): show queued runs as queued and a failed latest run as failed (#16416)
## Description

Fixes two status mismatches from the v5.0 bug bash
([ENG-4393](https://linear.app/onyx-app/issue/ENG-4393/activity-feed-shows-in-progress-for-runs-that-have-not-started-and-the)).

**Why**
- The activity feed titled queued runs "Indexing in progress." with a
spinner, while the status page counted 0 indexing and showed them as
"Scheduled · Nth in queue".
- The connector page header read "Indexed" after a single failed run,
while the status page row and the failure beside it said failed.

**What**
- A run that has not started is "queued" in the feed: "Indexing queued."
(and the same for syncs, pruning and checks, in every locale) with the
Scheduled tag's purple clock.
- `ccPairStatusTag` reads Failed when the latest run failed, a failed
first run included (the pair stays Initial Indexing), matching
`_connector_status`'s bucket on the status page. Deleting, paused and
invalid keep their tags.
- `statusTag.test.ts` pins the header cases.

## How Has This Been Tested?

Jest: `statusTag.test.ts` fails on main for the single-failure and
first-run cases and passes here (61 passed across the connector and i18n
suites). Live on dev1 data with this branch's web against dev1's API:
two real queued attempts read "Indexing queued." with the clock, and E2E
OAuth Drive (latest run failed, not in repeated error) reads Failed.
With that pair briefly set to INITIAL_INDEXING, main read "Initial
Indexing" and this branch "Failed".

| Before | After |
|---|---|
|
![before](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/activity-queued-and-failed-header/before-queued.png)
|
![after](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/activity-queued-and-failed-header/after-queued.png)
|
|
![before](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/activity-queued-and-failed-header/before-header.png)
|
![after](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/activity-queued-and-failed-header/after-header.png)
|

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.
2026-10-21 21:29:35 +00:00
Wenxi ad9c0030e9 feat(craft): simplify scheduled task details and plugin controls (#16408)
## Description

<img width="1014" height="660" alt="Screenshot 2026-10-21 at 12 10
14 PM"
src="https://github.com/user-attachments/assets/d885f4ef-8d88-49ee-aab8-4245e755a75b"
/>
<img width="1014" height="1200" alt="Screenshot 2026-10-21 at 12 10
03 PM"
src="https://github.com/user-attachments/assets/14b6d481-8d11-4d4d-b267-1b9cb9d2926f"
/>


fixes:
https://linear.app/onyx-app/issue/ENG-4406/runs-of-an-existing-scheduled-task-are-hard-to-find


Make scheduled tasks easier to inspect and edit. The detail page now
puts a compact prompt preview and enabled plugin labels above run
history. Its header keeps Edit, Run once, and Pause or Resume visible.

The editor groups schedule controls in a panel and replaces the app list
with plugin cards. Connected, enabled cards have a white background.
Disabled cards use the gray surface. Disconnected cards show an orange
dot and a connect button instead of the enable toggle. Clicking a card
opens action settings or the shared connection dialog.

Keep task drafts and modal selection in the editor, with separate
components for cards, action edits, and the read-only summary. Missing
connection data cannot enable a plugin. Editing actions before first
enabling now preserves untouched personal defaults. Update all nine
translations and the feature map.

## How Has This Been Tested?

- 33 tests across task policies, the summary, detail page, task form,
and schedule helpers.
- 9 translation catalog tests.
- TypeScript type and coverage checks, lint, formatting, translation
checks, and feature-map integrity.
- Playwright browser checks at desktop and mobile widths: prompt
expansion, edit navigation, schedule inputs, plugin toggles, connection
modal, card backgrounds, hover, and pointer cursor. No horizontal page
overflow.
- Browser checks used intercepted fixture data. No real scheduled run or
external OAuth sign-in was performed.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.



<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Redesigns the scheduled task detail page and editor so tasks are easier
to inspect and edit. The detail page now shows an expandable prompt
preview and enabled plugin labels above run history, with no tabs, and
the header keeps Edit, Run once, and Pause or Resume visible.

Plugin editing
- The app list is now a grid of plugin cards: connected and enabled
cards use a white surface, disabled cards use the gray surface, and
disconnected cards show an orange dot with a connect button.
- Clicking a connected card opens its action settings even when the
plugin is disabled for the task; clicking a disconnected card opens the
shared connection dialog.
- Disconnected plugins can still be disabled, but missing connection
data blocks their action settings.
- First edit or enable snapshots personal defaults, including untouched
actions, so they survive an enable toggle.
- Splits the editor into `TaskPluginCard`, `TaskAppActionsModal`, and
`TaskActionPoliciesSummary`, and updates all nine translations and the
feature map.

Schedule and load handling
- The schedule editor now groups controls in a panel and notes that
times use the local timezone.
- Action-policy load failures show an error with a retry button, and the
detail page links to Edit instead of using a tab.

<sup>Written for commit d2575959fb.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16408?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/16408?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16408&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**
- Redesigned scheduled-task details with collapsible prompts, run
history, status controls, editing, one-time runs, and deletion actions.
- Added plugin cards, connection flows, enablement controls, and
action-policy editing with unsaved-change protection.
- Added clearer interval, daily, and weekly schedule editing with
local-time guidance.
  - Added retry handling for action-policy loading errors.

- **Improvements**
  - Added configurable tag sizes for compact or standard displays.
  - Updated terminology and translations across supported languages.

- **Documentation**
  - Expanded guidance for scheduled-task screens and plugin behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 21:01:09 +00:00
Evan LohnandClaude Opus 5.5 c7f9a0e859 fix: serialize default assistant tool updates (#16336)
## 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>
2026-10-21 20:50:10 +00:00
Wenxi cee959d9a7 fix(plugins): simplify connection status before sign-in (#16412)
## Description

Show one **Needs sign-in** indicator and tooltip until a member connects
a plugin. After connection, show the existing Main Chat, custom agents,
and Craft status breakdown. Checking and unavailable connections also
use one status. Admin availability indicators stay unchanged.

Remove the generic readiness sentence from plugin previews. Connected
MCP, OpenAPI, and Craft App previews all use **Ready to use** as the
title and the same account text. Personal credentials and OAuth
pass-through show **Uses your connected account**. Workspace credentials
show **Workspace connection**. No-auth plugins show **No authentication
required**. Update all locales and the feature map.

## How Has This Been Tested?

- `cd web && bun run test --runInBand
src/sections/plugins/MemberPluginsPage.test.tsx
src/sections/plugins/MemberPluginDetailModal.test.tsx
src/i18n/__tests__/catalog.test.ts` — 102 tests passed.
- Reran `MemberPluginsPage.test.tsx` after adding connection-transition
assertions — 37 tests passed. Coverage checks the single sign-in
indicator for MCP OAuth, MCP credentials, OpenAPI, and Craft Apps, plus
the restored surface breakdown after authentication.
- After unifying connected titles and account copy, reran all three
suites above — 110 tests passed, including title and subtext coverage
across all plugin kinds and ownership modes.
- Changed-file pre-commit checks passed, including TypeScript type
checking and coverage, oxlint, oxfmt, translation parity, and
feature-map integrity.
- `python3 tools/i18n/check_copy_changes.py --base origin/main` and `git
diff --check` passed.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Simplifies plugin connection status so plugins that need sign-in show a
single Needs sign-in indicator instead of a premature surface breakdown.
Connected plugins keep the existing Main Chat, Custom agents, and Craft
status details; checking and unavailable connections also use one
status, while admin availability indicators stay unchanged.

- Removes the generic readiness sentence from connected previews;
connected MCP, OpenAPI, and Craft App cards now show "Ready to use" with
one ownership line: "Uses your connected account", "Workspace
connection", or "No authentication required".
- Replaces the unused `readyDescription` translation with
`noAuthentication` in all locales and updates the feature map to match
the simplified pre-connection status.

<sup>Written for commit ff0dae0e9c.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16412?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/16412?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16412&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

- **Improvements**
  - Plugin cards now show a single status indicator until connected.
- Connected plugins display availability details for each supported
surface.
- Connected integrations consistently show “Ready to use” with clearer
ownership information.
  - Sign-in-required states now provide clearer indicators and tooltips.
  - Unsupported surfaces are clearly labeled as unavailable.
  - Plugins that do not require authentication are labeled accordingly.
- **Localization**
- Updated plugin connection and authentication messaging across
supported languages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 20:49:55 +00:00
Evan LohnandClaude Opus 5.5 0fe1f34bed fix: stop two Playwright test races (#16333)
## 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>
2026-10-21 20:25:57 +00:00
Wenxi 192f505038 fix(craft): preserve session drafts and support new-tab navigation (#16410)
## Description

fixes:
https://linear.app/onyx-app/issue/ENG-4405/a-craft-chat-cannot-be-opened-in-a-new-tab

Rename the Craft sidebar action to **New Session** and make it a link.
Users can open it in another tab through the context menu or
Ctrl/Command-click. Same-tab navigation keeps the existing
unsaved-change prompt. Update the label in every locale.

Keep a separate text draft for each Craft session and for New Session.
Previously, navigation could discard the welcome draft or carry one
session’s text into another. Reuse the draft-storage hook with immediate
writes so quick navigation preserves the latest text. Drafts survive
navigation and reloads within the browser tab. Sending or successful
queueing clears the saved text; rejected queueing retains it.

Draft keys include the signed-in account. Logout removes current and
legacy Craft drafts, so the next account cannot restore them. Failed
slash-token deletion preserves redo history.

Update the Craft feature map and test the composer with the real input
component.

## How Has This Been Tested?

- Composer, base input, draft storage, chat panel, editor undo/redo, and
logout tests: 82 passed.
- Sidebar and translation catalog tests: 12 passed.
- Pre-commit checks, including TypeScript and type coverage, lint,
formatting, translations, and feature-map integrity.
- Frontend preview on port 3011 against the local API on port 8080:
Craft page and proxied health endpoint returned HTTP 200.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Renames the Craft sidebar action to **New Session** and makes it a link,
so users can open it in a new tab with Ctrl/Command-click or the browser
context menu. Same-tab navigation keeps the existing unsaved-change
prompt.

Keeps a separate composer text draft for each Craft session and for New
Session, so navigation no longer discards the welcome draft or carries
one session's text into another. Drafts survive navigation and reloads
within the tab; sending or accepted queueing clears the saved text,
while a rejected submission keeps the text and attachments. Paste-tile
edits and slash-query removals also update the saved draft immediately.

Draft keys are scoped to the signed-in account, and logout clears
current and legacy Craft drafts so the next account can't restore them.
A failed slash-token deletion preserves redo history.

<sup>Written for commit 4a51da81fb.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16410?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/16410?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16410&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**
* New Session navigation supports new tabs and modifier-click behavior.
* Drafts persist separately across sessions, navigation, and reloads for
signed-in users.
  * Failed submissions retain message text and attachments for retry.
* Unsaved-change protection remains available during same-tab
navigation.
* **Bug Fixes**
* Attachment drafts clear when starting, switching, or ending a session.
* Composer content resets and restores correctly when switching
sessions.
  * Signing out clears saved Craft drafts.
* **Localization**
  * Updated the New Session label across supported languages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 20:22:38 +00:00
Nikolas Garza e4fb3f276b fix(indexing): exact run durations, 24h/7d/all-time windows, pinned status page history (#16381)
## Description

Fixes from the v5.0 bug bash on the connector detail and indexing status
pages
([ENG-4392](https://linear.app/onyx-app/issue/ENG-4392/detail-page-goes-stale-when-a-run-ends-and-run-states-stick)).

**Why**
- Sub-minute runs read "(0 min)", and canceled runs showed the running
icon, so a row's state was hard to read.
- The status page's history panel opened at the top of the page, out of
view when the list was scrolled, and View Logs could not close it.

**What**
- Run rows print their duration to the second ("47s", "4m 37s", "11h
34m"). Canceled runs show their kind's icon.
- The connector page's chart and run list open on the last 24 hours and
offer 24 hours, 7 days and all time. `12h` is gone from the window enum
(it shipped only in the v5.0 betas).
- `all` starts at the pair's oldest kept run, at least a day back. The
fleet overview refuses it, since it would read every kept attempt of
every pair.
- The status page's history panel pins under the header while the list
scrolls. View Logs on the open connector closes it. The expanded row's
run search gets 0.5rem padding.

Not changed: the detail page already polls every 5 s, so run state
updates within 5 s, not instantly.

## How Has This Been Tested?

External-dependency tests on Postgres cover the 24h window's partial
overlap, the all-time start and empty pair, and the overview's refusal
(32 passed with the throughput and route contract suites). Live on dev1
data with this branch's API and web: the rows, the windows and the
all-time chart below, the overview returning 400 for `all`, the panel
measured pinned at 24px through the whole scroll, and the View Logs
toggle. The updated Playwright spec has not run locally.

| Before | After |
|---|---|
|
![before](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-bugbash-run-fixes/before-runs.png)
|
![after](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-bugbash-run-fixes/after-runs.png)
|
|
![before](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-bugbash-run-fixes/before-window.png)
|
![after](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-bugbash-run-fixes/after-window.png)
![after](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-bugbash-run-fixes/after-all-time.png)
|
|
![before](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-bugbash-run-fixes/before-history.jpg)
|
![after](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-bugbash-run-fixes/after-history.jpg)
|
|
![before](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-bugbash-run-fixes/before-row-search.png)
|
![after](https://raw.githubusercontent.com/onyx-dot-app/onyx/assets/ui-screenshots/indexing-bugbash-run-fixes/after-row-search.png)
|

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.
2026-10-21 20:07:55 +00:00
onyx-cherry-pick[bot] c861af5d1d fix(deps): pin storybook to 10.6.2 (#16394)
Pins `storybook` to `10.6.2` in every manifest and lockfile for
[GHSA-f2ph-cmg5-wxc8](https://osv.dev/vulnerability/GHSA-f2ph-cmg5-wxc8).
Opened by the [CVE Alerts
run](https://github.com/onyx-dot-app/onyx/actions/runs/39650448612).

Closes #16393

I bumped the only `storybook` pin in the repo, a devDependency in
`web/package.json`, from 10.5.0 to 10.6.2. That is the fixed release for
the 10.x line in GHSA-f2ph-cmg5-wxc8. I checked the other `package.json`
files (root, `web/lib/opal`, `web/lib/shared`, `widget`,
`examples/widget`, `desktop`, `mobile`) and none of them pin storybook.
The bug only affects a running `storybook dev` server open in a browser:
a malicious website can send it messages that change story files on the
developer's machine. Onyx only runs Storybook through the `storybook` /
`storybook:build` scripts, and the web `Dockerfile` runs only `next
build` and ships the Next.js standalone output, so production
deployments are not affected. Developers who run `bun run storybook`
locally are exposed until they upgrade. The other Storybook packages
(`@​storybook/react`, `@​storybook/react-vite`,
`@​storybook/addon-docs`, `@​storybook/addon-themes`) stay on 10.5.0, so
check that the regenerated lockfile resolves cleanly and consider
aligning them to 10.6.2 in a follow-up.


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Pins `storybook` from `10.5.0` to `10.6.2` in `web/package.json` and
regenerates `web/bun.lock` to fix
[GHSA-f2ph-cmg5-wxc8](https://osv.dev/vulnerability/GHSA-f2ph-cmg5-wxc8),
which let a malicious website modify story files on a developer's
machine through a running `storybook dev` server.

- The `storybook` pin in `web/package.json` is the only one in the repo;
no other manifest references it.
- Production deployments are not affected: the web `Dockerfile` only
runs `next build` and ships the Next.js standalone output.
- The related Storybook packages (`@storybook/react`,
`@storybook/react-vite`, `@storybook/addon-docs`,
`@storybook/addon-themes`) remain on `10.5.0`; consider aligning them in
a follow-up once the regenerated lockfile resolves cleanly.

<sup>Written for commit 02b614a295.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16394?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/16394?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16394&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

* **Chores**
  * Updated the Storybook development tooling to version 10.6.2.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: onyx-cherry-pick[bot] <285052878+onyx-cherry-pick[bot]@users.noreply.github.com>
2026-10-21 18:56:30 +00:00
Wenxi 9b932c4443 fix(craft): restore progress and pending connections after refresh (#16380)
## Description
fixes:
https://linear.app/onyx-app/issue/ENG-4404/craft-session-dies-after-a-refresh-mid-run-with-no-progress-indicator

Refreshing Craft during a tool call can hide progress and consume the
only announcement for a pending connection request. The session then
appears stuck even while its turn remains active.

Show the existing flashing cursor while the turn is active, including
quiet tool calls and final history loading. Preserve its original
vertical alignment and avoid a duplicate cursor beside streaming text.
Restore pending Connect app cards from cache when the browser attaches,
without duplicating live announcements. Expired, resolved, and
ended-turn requests are excluded. Turn cleanup removes only its own
requests. The session index covers the longest pending request,
including configured waits longer than 30 minutes. Cache failures retry
in the announcement pump without closing the event stream.

If an active-turn status read fails, keep the session in its existing
retry state with typing and submission disabled. Ignore late history
errors after a newer turn takes ownership.

This uses the existing attachment and cache paths. It adds no client
polling, packet replay buffer, or database migration.

## How Has This Been Tested?

- 39 backend unit tests passed across the stream merger, tenant context,
and permission handling suites.
- 4 external-dependency tests passed against the local kind Postgres and
Redis services. Coverage includes concurrent pending requests, consumed
announcements, payload preservation, TTL bounds, resolution, expiry, and
long-lived requests added alongside shorter requests. The cancellation
regression verifies that stopping a turn removes its card without
removing another producer’s request.
- 312 frontend tests passed across the message list, session store,
streaming hook, chat panel, and real input component. Coverage includes
failed status reads, stale failures, duplicate and distinct connection
requests, progress indicators, and disabled typing and submission.
- Temporarily removed restoration to confirm that the restored-only
regression test fails; restored the implementation and reran the backend
tests successfully.
- Changed-file pre-commit checks passed, including typing, lint,
formatting, and feature-map integrity.

Full deployed Kubernetes reconnect and Playwright refresh flows were not
run. The current Kubernetes lane lacks deterministic connection-request
setup; the real-cache tests are external-dependency tests, not deployed
integration coverage.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes Craft's refresh behavior so a page refresh no longer drops pending
connection cards or makes an active turn look idle.

**Restores pending connection cards after refresh**
- Connect-app requests are now indexed per session in Redis so new
attachments can restore still-actionable cards.
- Pending records and the session index expire with the permission wait;
resolved, expired, and cross-session requests are excluded.
- Turn cleanup removes only its own requests, including when the user
stops the turn.
- Restored cards are deduplicated against live announcements by request
ID, and malformed permission requests are rejected.

**Keeps an active turn visible after refresh**
- The message list reuses the existing flashing cursor while a turn is
active, including after refresh during a quiet tool call; streaming text
keeps its own cursor without a duplicate.
- Failed active-turn status reads now trigger session-load recovery
instead of treating the turn as idle.
- Typing and submission stay disabled until recovery succeeds, with an
explicit retry action exposed.

<sup>Written for commit a11b82e67d.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16380?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/16380?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16380&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**
- Pending Connect app requests now return after refreshing or
reconnecting to a session, while resolved or expired requests are
excluded.
- Active-turn progress and cursor indicators are preserved across
refreshes.
  - Recovered requests are prevented from appearing twice.

- **Bug Fixes**
- Failed session loads expose retry actions and disable message
submission until recovery succeeds.
  - Draft messages remain preserved while input is temporarily disabled.
- Malformed permission requests are rejected safely, and cancelled turns
clean up only their own requests.

- **Documentation**
- Updated documentation to describe session recovery, request
restoration, expiration, and streaming behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 18:55:47 +00:00
Wenxi 521a42b705 fix(connectors): route Google OAuth callbacks by connector path (#16379)
## Description

Fix Google Drive authorization failing with “Request did not pass CSRF
verification” when using a shared Google credential.

The callback selected Gmail whenever the full URL contained `gmail`.
Shared Google credentials request both Drive and Gmail scopes. A Drive
callback containing `gmail.readonly` therefore reached the Gmail
handler, which expected a different credential cookie.

Select the handler from the `[connector]` route parameter and reject
unsupported connectors. Preserve the callback query and cookies. Add
regression tests and update the feature map.

## How Has This Been Tested?

- All three callback regression tests pass: Drive and Gmail with
combined scopes, plus unsupported connector rejection.
- Confirmed that the Drive regression test fails with the old routing
logic.
- All pre-commit checks pass, including TypeScript type checking and
coverage, formatting, linting, and feature map integrity.
- Completed real Google OAuth on a fresh isolated Docker-backed ODS
stack from this branch. The callback included Gmail scopes, reached the
Drive handler successfully, and returned to Drive without an OAuth
failure. The Drive auth-check endpoint then returned `authenticated:
true`.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes Google Drive OAuth authorization failing with "Request did not
pass CSRF verification" when using a shared Google credential.

The callback previously chose the Gmail handler whenever the full URL
contained "gmail". Shared Google credentials return scopes for both
Drive and Gmail, so a Drive callback including "gmail.readonly" was
routed to the Gmail handler, which expects a different credential
cookie. The handler is now selected from the `[connector]` route
parameter, and unsupported connectors are rejected with a 404 before any
backend call.

**Testing**
- Regression tests cover Drive and Gmail callbacks with combined scopes,
plus rejection of unsupported connectors.
- The Drive regression test fails under the old routing logic.

<sup>Written for commit 053e5fa90a.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16379?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/16379?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16379&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

* **Bug Fixes**
* Corrected OAuth callback handling for Gmail and Google Drive
connectors.
* Unsupported connector routes now return a 404 response instead of
proceeding through authentication.
* Valid callbacks continue to forward authorization details and cookies,
then redirect successfully.

* **Documentation**
* Added guidance clarifying connector-specific Google OAuth callback
behavior and shared credential scopes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 17:44:29 +00:00
roshan 835df87272 fix(web): close tool menus before opening the tool credentials dialog (#16371)
## Description

Part 6 of the
[ENG-4416](https://linear.app/onyx-app/issue/ENG-4416/ux-plugins-and-tool-flow-nits)
stack. With the tools menu and the hover-opened Plugins submenu open,
clicking "Tools need credentials" opened the "Connect tools" dialog
under both menus. Radix closes a popover on the outside `click`, but the
dialog opened in the same click handler and trapped focus first, so the
menus never got the dismiss. `MCPConnectionPrompt` now opens the dialog
on the next task, after the menus close.

| Before | After (desktop) | After (mobile) |
| --- | --- | --- |
|
![before](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8zNjM3ZjE1Ni1kZWFhLTQwMzUtODRjMi04Y2ZlNTlmYTg4MjMiLCJpYXQiOjE3OTI1NTc0NTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJob3Zlci1jb25uZWN0LnBuZyJ9.r2EvmuEsALJ1DsioRWwguP-GypdMePED_umWIZASOJg)
|
![after](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9iNjBlMjVlYi01OWZmLTRmNDctOTk1ZS1mYWE1Mzg0ZWM2ZGMiLCJpYXQiOjE3OTI1NTc0NTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJjcmVkZW50aWFscy0xLWRpYWxvZy1kZXNrdG9wLnBuZyJ9.B9ad5MvaNajCEzJY8MpMJ4PirHziHRPWXIbRMOCoZZU)
|
![mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8zMThlZmRiNC1lN2M4LTRjODItODEwMS0zNWU0YmE2M2ZlYmMiLCJpYXQiOjE3OTI1NTc0NTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJjcmVkZW50aWFscy0xLWRpYWxvZy1tb2JpbGUucG5nIn0.khBwpQ8J3SQXqBZI8yTCUc5btFyam2ZMGutsF7vT2PI)
|


![flow](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS81NDE3ODJiNC03Yjg2LTQzMmUtYTM5Mi0wODJjNzJkYjNjYjAiLCJpYXQiOjE3OTI1NTc0NTgsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhZnRlci1jcmVkZW50aWFscy1kZXNrdG9wLndlYnAifQ.av3wwa4WljrcDk3DAd2U9CSp7ka6P4cpDN7Dy2e6mFs)

## How Has This Been Tested?

- Local ODS stack with a bearer-token MCP server that needs per-user
credentials. Before the click both menus are `open`; after it there are
no popovers and one "Connect tools" dialog (desktop hover + mobile tap).
- Also checked the submenu's own "Connect" button: menus close and the
"Connect Wiki MCP" dialog opens.
- MCPConnectionPrompt suite: 11 passed.
- `pre-commit` on touched files.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.


Link to Devin session:
https://app.devin.ai/sessions/09df74c24e19407bbf0e7ab775b5cb0a
Open in Devin Desktop:
https://app.devin.ai/desktop/session/09df74c24e19407bbf0e7ab775b5cb0a?variant=devin
Requested by: @rohoswagger

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes the "Connect tools" dialog opening underneath the tools menu and
the hover-opened Plugins submenu. Deferring the dialog open with
`window.setTimeout` lets Radix dismiss the open popovers on the outside
click before the dialog traps focus, so the dialogs no longer stack over
the menus.

<sup>Written for commit c0d66ac0e9.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16371?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/16371?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16371&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

* **Bug Fixes**
* Improved the transition from open popovers to connection dialogs,
ensuring popovers close before the dialog captures focus.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 17:06:17 +00:00
roshan 661c096179 fix(web): align plugin rows and the Plugins entry in the tools menu (#16370)
## Description

Part 5 of the
[ENG-4416](https://linear.app/onyx-app/issue/ENG-4416/ux-plugins-and-tool-flow-nits)
stack. Two alignment fixes:

- `PluginRowLayout` rendered an empty description line, so a plugin with
no description had its title pushed up next to the icon. The line now
renders only when there is a description.
- The "Plugins" row in the chat tools menu used `padding={0}` and a bare
chevron, so its icon, label and chevron sat 2-4px off the other rows. It
now uses `variant="section"` like `ToolLineItem`, and the chevron sits
in the same 24px slot as the row buttons.

Measured in the browser: icon x and right-icon x/y now match the other
rows (535 / 729 desktop, 63 / 257 mobile); a title-only row's title is
centered on the icon.

| Before | After (desktop) | After (mobile) |
| --- | --- | --- |
|
![before](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS83MDRhNjU2Yi1mMjNhLTQ3ZGYtYjRlZi0xZWFiMTk4NjQzYTkiLCJpYXQiOjE3OTI1NTg2NDUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJ0b29scy1wb3BvdmVyLnBuZyJ9.Pt6KAswIFuX0mHFvRyJbe7uT93m4VqVWPj74mjBVAGw)
|
![after](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS81NDk0OGM5OS1kMmRjLTRiZDMtYjg4Ny0wY2RlZjMyZGZjYTYiLCJpYXQiOjE3OTI1NTg2NDUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJ0b29scy1wb3BvdmVyLWRlc2t0b3AucG5nIn0.mL6FjV7ec_FL3xoiup7DUnUnnDsLDR_1Nwh96RSseSw)
|
![mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8yMDFiM2U2OS03ZTE3LTRiODAtYmNiOS03N2NmNDRiMmJhZGUiLCJpYXQiOjE3OTI1NTg2NDUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJ0b29scy1wb3BvdmVyLW1vYmlsZS5wbmcifQ.q_WRoQIBajheDbIzsGrGVAzTF3S1K-US2KwKJfKNyko)
|
|
![before](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS81Y2JlODZiNi04YjdhLTRmYTgtODg1Mi1jZmIzMDFmZGY2ZTkiLCJpYXQiOjE3OTI1NTg2NDUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhZG1pbi1wbHVnaW5zLnBuZyJ9.pHcJMbNq80QBSuaWVi-woCfP3V3Lvi4ZrnhSHDO90BA)
|
![after](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9jODYyOTM2MS04OGIxLTRjYTUtYTYwZS1mOTJkN2E4ODhjMDciLCJpYXQiOjE3OTI1NTg2NDUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhZG1pbi1wbHVnaW5zLWRlc2t0b3AucG5nIn0.ZKZphyb5HuRkweyPX6AgvPFJEsbK0lQrJusq1qoAszM)
|
![mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8yYjc1NjI3My0wNzE0LTQxMzItODQ3Yy1lMjQ0MjZhYTk4M2MiLCJpYXQiOjE3OTI1NTg2NDUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhZG1pbi1wbHVnaW5zLW1vYmlsZS5wbmcifQ.3pr3OSYCASp32h5mqfWBI8Y_AFvChT1TUIDusnv1MU8)
|

Member page:
![member](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9iZjgxMzk3Yi02NTI1LTQ4MDQtOTBjZi1jM2FiOGVjNTc4NWUiLCJpYXQiOjE3OTI1NTg2NDUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhcHAtcGx1Z2lucy1kZXNrdG9wLnBuZyJ9.Hl3hIH1vWkE710ByoI0RiZto4Fi-ni9UPwkzweUB0Bw)

## How Has This Been Tested?

- Local ODS stack: admin and member Plugins pages and the chat tools
menu, desktop + mobile, with bounding-box checks.
- New `PluginRowLayout.test.tsx`; ChatPluginsPopover, PluginsPage and
MemberPluginsPage suites: 81 passed.
- `pre-commit` on touched files.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.

### Mobile Plugins submenu

Browser QA found one more problem at 390px. The submenu had no room
beside the tools menu, so it shrank to about 94px and the plugin names
were hidden. On phones (`useScreenSize().isMobile`), the submenu now
opens above the Plugins row, over the tools menu, at full width. Desktop
still opens it to the right.

Probe at 390px: the submenu is `side=top`, 240px wide. `Wiki MCP` and
`Metadata API` are visible. Connect closes both menus and opens one
dialog. On desktop the submenu is still `side=right`, 240px wide.

| 390px, after | 390px video |
|---|---|
| ![mobile
submenu](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9jNDhhMGE5NC0wNjM1LTQxOTItODIzNS1jNjdmMjZhMTU3NWUiLCJpYXQiOjE3OTI1NTg2NDUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJzdWJtZW51LW1vYmlsZS5wbmcifQ.W7FvSf0ssSb3Ss9oc0ONKE0ogUdVggII0vM7pf4MoLM)
| ![mobile submenu
video](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS81YmM0MjU4Ny03MjliLTQ1NGYtYTFjOS0wNDViMThiYTc3MzAiLCJpYXQiOjE3OTI1NTg2NDUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJzdWJtZW51LW1vYmlsZS1hZnRlcjIud2VicCJ9.JTlLs_pfh7TiY1tiIy7__C8uswo1dy4XofR-nzCDry4)
|

Link to Devin session:
https://app.devin.ai/sessions/09df74c24e19407bbf0e7ab775b5cb0a
Open in Devin Desktop:
https://app.devin.ai/desktop/session/09df74c24e19407bbf0e7ab775b5cb0a?variant=devin
Requested by: @rohoswagger

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes plugin row alignment so plugins without descriptions no longer
push their titles up against the icon, and the Plugins entry in the chat
tools menu now lines up with the other rows.

- Plugins without a description no longer render an empty line below the
title.
- The Plugins row in the tools menu now uses `variant="section"` and a
24px chevron slot, matching the icon and chevron positions of the other
rows.
- On phones, the Plugins submenu now opens above the tools menu at full
width, since there was no room beside it; desktop still opens it to the
right.
- Adds tests for `PluginRowLayout` description rendering.

<sup>Written for commit 4b72f53be2.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16370?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/16370?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16370&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

- **Bug Fixes**
- Plugin rows no longer display an empty description area when no
description is available.
- Plugin descriptions continue to appear beneath their titles when
provided.
- Improved alignment and spacing for the chat plugins popover trigger
and chevron.
- The chat plugins popover now uses responsive positioning, opening
above on mobile and remaining right-aligned on larger screens.

- **Tests**
  - Added coverage for plugin rows with and without descriptions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 17:06:16 +00:00
roshan efc66222bf fix(web): list every connector source in Chat Preferences and explain the section (#16369)
## Description

Part 4 of the
[ENG-4416](https://linear.app/onyx-app/issue/ENG-4416/ux-plugins-and-tool-flow-nits)
stack. Chat Preferences > Connectors showed only the first 3 sources and
gave no context. It now:

- lists every connected source and wraps,
- explains "Sources that Internal Search uses in chat. Add or change
them on the indexing status page.",
- labels the link "Manage connectors".

| Before (desktop) | After (desktop) | Before (mobile) | After (mobile)
|
| --- | --- | --- | --- |
|
![before](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8zYmVmYzlmNS04MGY3LTRjNGQtOGM3MC1lMzdmM2E5Y2NhNWYiLCJpYXQiOjE3OTI1NTczOTUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJjaGF0LXByZWZzLWNvbm5lY3RvcnMtZGVza3RvcC5wbmcifQ.NTsKFI5JzY2Y__aKena76nJG4xRr3tHP3rN3vhgy0bk)
|
![after](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8wNWE5MzQwNC1iNTgzLTRlZjgtYTZlNC0zYzk0MzA5MTk1OTYiLCJpYXQiOjE3OTI1NTczOTUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJjaGF0LXByZWZzLWNvbm5lY3RvcnMtZGVza3RvcC5wbmcifQ.Y8yEo_wUBHE0TG5pS--AAeV_IhKfqHaK9mbKSUnhvzE)
| ![before
m](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8yZDBkYzJkMC1kNzM3LTQ0NjctODIyMy1mMDNkNTViNTNkYTQiLCJpYXQiOjE3OTI1NTczOTUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJjaGF0LXByZWZzLWNvbm5lY3RvcnMtbW9iaWxlLnBuZyJ9.7wB8KLy_kvH0cko5va9YPjwzjM7d1vEGwS64cq2nwaI)
| ![after
m](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8xNjAwOWZkYS02MjA0LTRhNzItYWI5Zi0zZmJjN2U3MmMxNmQiLCJpYXQiOjE3OTI1NTczOTUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJjaGF0LXByZWZzLWNvbm5lY3RvcnMtbW9iaWxlLnBuZyJ9.L7AILGvADUk8rI5MOd8rbP-kwFHvq651RYNkyaY9PXQ)
|


![flow](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS82NjBjMGE2My0wMzA3LTQ5MGQtYTg5OC1kNGQyZTQ2ODQ4NjEiLCJpYXQiOjE3OTI1NTczOTUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhZnRlci1jaGF0cHJlZnMtZGVza3RvcC53ZWJwIn0.IDGmU6JHq5fG8Gbv-X7KPe8n8r_96mFwpyhKIUSsq74)

## How Has This Been Tested?

- Local ODS stack with 7 connector sources: before, 3 of 7 showed;
after, all 7 show on desktop and mobile.
- New jest test `lists every connector source and explains the section`;
ChatPreferencesPage suite passes.
- `pre-commit` on touched files.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.


Link to Devin session:
https://app.devin.ai/sessions/09df74c24e19407bbf0e7ab775b5cb0a
Open in Devin Desktop:
https://app.devin.ai/desktop/session/09df74c24e19407bbf0e7ab775b5cb0a?variant=devin
Requested by: @rohoswagger

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Makes Chat Preferences list every connector source instead of showing
only the first three, explains what the section is for, and stacks the
header on narrow screens. The management link is now labeled "Manage
connectors", only shows when sources exist, and points to the indexing
status page. Tests cover the full list, description, and link behavior.
Part of the ENG-4416 UX cleanup.

<sup>Written for commit d02900dff9.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16369?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/16369?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16369&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 now display all available connector sources.
- Added guidance explaining that connectors power Internal Search and
are managed from the indexing status page.
- Updated the management link label to “Manage connectors” and linked it
to the indexing status page.
- The management link remains hidden when no connectors are available;
the empty state is unchanged.
  - Updated connector-related text across supported languages.

- **Tests**
- Added coverage for connector listings, guidance, and management
navigation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 17:06:15 +00:00
roshan aaa78990a6 fix(chat): show blocked and redirected custom tool calls as failed (#16368)
## Description

Part 3 of the
[ENG-4416](https://linear.app/onyx-app/issue/ENG-4416/ux-plugins-and-tool-flow-nits)
stack. A custom tool call blocked by the SSRF check (or answered with a
redirect) raised `ToolCallException` before any `CustomToolDelta`, so
the step list said "get_meta_data completed".

- `CustomTool.run()` now returns a failed `CustomToolCallSummary` and
emits a `CustomToolDelta` with `CustomToolErrorInfo` for blocked
destinations (`status_code=None`) and redirects (the 3xx code). This
reuses the existing 401/403 error path, so the error also persists in
history.
- `CustomToolRenderer` shows "{tool} was blocked" with the error icon
and error surface. HTTP failures keep "{tool} failed ({code})".
- The LLM gets the same short message the user sees.

| Before | After | After reload | Mobile |
| --- | --- | --- | --- |
|
![before](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8xN2M4NzBmOC1mN2QxLTRhZmMtOTJlOS05MjA3ZjIyOTY0MDYiLCJpYXQiOjE3OTI1NTczOTIsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJibG9ja2VkLXRvb2wtZG9uZS5wbmcifQ._FurQrQrApzCTY8kfPRGGceyXv67CDXPMWarqGRUUag)
|
![after](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS80OGE0MTRhMS1lNWI5LTRkOTItYjhiYy0zOWJmMTg2ZDI3NjMiLCJpYXQiOjE3OTI1NTczOTIsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJibG9ja2VkLXRvb2wtZXhwYW5kZWQtZGVza3RvcC5wbmcifQ.w3cJq2pLwzEQ0tTFWZhFen_3Er4C2obm7e5Fj8PikN4)
|
![reload](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS80OTMwMDJjMC1kYmVjLTQxZmMtYWIwNy1mOGYzODE4Y2Y5NWMiLCJpYXQiOjE3OTI1NTczOTIsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJibG9ja2VkLXRvb2wtcmVsb2FkLWRlc2t0b3AucG5nIn0.G6jDHd5gN_Dre0utwEEP0H1kT60stetm-L-7Pqzj6iQ)
|
![mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9lMWM1NjQwNS1hM2FhLTQ5YjgtYWU0Mi1iNDBhZjRlYzA4OTUiLCJpYXQiOjE3OTI1NTczOTIsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJibG9ja2VkLXRvb2wtZXhwYW5kZWQtbW9iaWxlLnBuZyJ9.6i7DBU4S-6P9fvgrfKT_U1QV9DgxS39apmJ16CnzL_0)
|


![flow](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8wMjhlZjM4Ni0zYTcyLTQ0NzItYTZlMi1mMTllODhiNzQ5OWMiLCJpYXQiOjE3OTI1NTczOTIsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhZnRlci1ibG9ja2VkLWRlc2t0b3Aud2VicCJ9.tFAbmOk535udvZk_wDwUrQ5MBvYyGyCWZ0-laqVKlLs)

The last "Done" row is the timeline's turn-finished step (`showDoneStep`
in `useTimelineUIState`), shown for every finished turn. It no longer
reads as the tool's result.

## How Has This Been Tested?

- Local ODS stack + gpt-4.1-mini: an OpenAPI plugin on
`169.254.169.254.nip.io`; asked chat to call it. Desktop, reload and
mobile show "was blocked" and no "completed"; API log shows the
metadata-IP refusal.
- `pytest backend/tests/unit/onyx/tools/custom`: 105 passed (blocked,
redirect, and delta/summary parity cases).
- New `CustomToolRenderer.test.tsx` (blocked + HTTP failure).
- `pre-commit` (ty, ruff, tsc, oxlint) on touched files.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.


Link to Devin session:
https://app.devin.ai/sessions/09df74c24e19407bbf0e7ab775b5cb0a
Open in Devin Desktop:
https://app.devin.ai/desktop/session/09df74c24e19407bbf0e7ab775b5cb0a?variant=devin
Requested by: @rohoswagger

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes custom tool calls blocked by the SSRF check or answered with a
redirect so they show as failed instead of "completed" in the chat step
list.

- `CustomTool.run()` now returns a failed `CustomToolCallSummary` and
emits a `CustomToolDelta` with error info for blocked destinations and
redirects instead of raising `ToolCallException`, reusing the existing
401/403 error path so the error persists in history.
- `CustomToolRenderer` shows "{tool} was blocked" with the error icon
and error surface; HTTP failures keep "{tool} failed ({code})".
- The LLM receives the same short message the user sees.

<sup>Written for commit 29b59437a1.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16368?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/16368?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16368&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

- **Bug Fixes**
- Custom tool requests blocked by security checks or redirects now
appear as structured failures instead of unexpected errors.
- Error details, including HTTP status codes where available, are
consistently shown in the conversation.
- Blocked and authentication-related errors now display clear,
non-collapsible status messages.

- **Localization**
  - Added blocked-tool status translations across supported languages.

- **Tests**
- Added coverage for blocked destinations, redirects, HTTP failures, and
error rendering.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 17:06:14 +00:00
roshan d586b06f83 fix(web): show the OpenAPI schema validation error once (#16367)
## Description

Part 2 of the
[ENG-4416](https://linear.app/onyx-app/issue/ENG-4416/ux-plugins-and-tool-flow-nits)
stack. The OpenAPI modal showed the server-side schema error (e.g. the
metadata-IP / SSRF refusal) twice: once from Formik on the field and
once as an extra `Text` under it. The async validation result now lives
in local `definitionError` state and renders once with `InputErrorText`,
and the textarea gets the error variant.

| Before | After (desktop) | After editing another field | Mobile |
| --- | --- | --- | --- |
|
![before](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS83ZTY1ZTNmNy1lZjliLTQ3YTctOWZkYy02OGNjNzJhYjM0MjkiLCJpYXQiOjE3OTI1NTczODksIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJvcGVuYXBpLWR1cC1lcnJvci5wbmcifQ.7SCkDWN0PDKrt57r3OX-kFHheMo5i0K2ShywUlOHkno)
|
![after](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS81MDYxYWM3MS00NTU4LTQ0MTAtODg2Ni0wMjNiZDIzY2Q5NTEiLCJpYXQiOjE3OTI1NTczODksIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJvcGVuYXBpLWVycm9yLWRlc2t0b3AucG5nIn0.3poZe8mqK420UWkbunK-YZfDnvI5xNFK2nR9AJmirG8)
| ![after
edit](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8wMmY0MzY3Mi03ZGVjLTQ5MjYtOWFjMC03MzIzNWQ4YzFhYmMiLCJpYXQiOjE3OTI1NTczOTAsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJvcGVuYXBpLWVycm9yLWFmdGVyLWVkaXQtZGVza3RvcC5wbmcifQ.O6jcKmj4wSLRd35KI_ur0P6PxkgI5BaEq9OrUv6x3Js)
|
![mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS9hZmEwZWYzZC1lODE2LTRiMGItOWE2Ny0xMmUyNDQ0NjA5YjUiLCJpYXQiOjE3OTI1NTczODksIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJvcGVuYXBpLWVycm9yLW1vYmlsZS5wbmcifQ.SmKRQG4vwxKFeIWZsV6eqNELgO9mx0bvLH3a4dYC61w)
|


![flow](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS84ZjJhMTEwNi1jMjQwLTQ4MDMtYWJlNS05NDQ0NDEyM2M5NTMiLCJpYXQiOjE3OTI1NTczOTAsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhZnRlci1vcGVuYXBpLWRlc2t0b3Aud2VicCJ9.WjTp3Ftdqzq3ufgZOMzlKBWoGHSzbdOIDloEZvCahRQ)

## How Has This Been Tested?

- Local ODS stack: pasted a schema whose server is
`http://169.254.169.254.nip.io`; the error shows once after input, blur,
editing another field, and Continue (desktop + mobile).
- New jest test `shows a schema validation error once before and after
blur`; create modal + detail suites: 53 passed.
- `pre-commit` on touched files.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.


Link to Devin session:
https://app.devin.ai/sessions/09df74c24e19407bbf0e7ab775b5cb0a
Open in Devin Desktop:
https://app.devin.ai/desktop/session/09df74c24e19407bbf0e7ab775b5cb0a?variant=devin
Requested by: @rohoswagger

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes the OpenAPI schema validation error showing twice in the modal —
once from Formik on the field and once as a separate message. The
server-side error (such as the metadata-IP / SSRF refusal) now lives in
local `definitionError` state, renders once with `InputErrorText`, and
the textarea gets the error variant. Stale validation errors are cleared
whenever the definition changes, so an outdated server response won't
linger while the schema is edited.

<sup>Written for commit 85451dc407.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16367?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/16367?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16367&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

- **Bug Fixes**
- Improved validation feedback for OpenAPI definitions, including
clearer formatting and invalid JSON errors.
- Validation messages now remain visible consistently when users move
between fields or update other inputs.
- Stale validation results are ignored to prevent outdated errors from
appearing.

- **Tests**
- Added coverage ensuring schema validation errors appear once, remain
singular after edits and blur, and are replaced by required-field errors
when the definition is cleared.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 17:06:13 +00:00
roshan 261d6c72af fix(web): call OpenAPI plugins plugins, not actions, in the Plugins flow (#16366)
## Description

Part 1 of the
[ENG-4416](https://linear.app/onyx-app/issue/ENG-4416/ux-plugins-and-tool-flow-nits)
stack. The OpenAPI flow lives on the Plugins page but said "Add Action".
The create/edit modal titles, submit button, toasts, auth-modal copy and
the Plugins "Add" menu now say "OpenAPI plugin" / "Add Plugin". All 9
locales are updated.

| Before | After (desktop) | After (mobile) |
| --- | --- | --- |
|
![before](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS8wMWFjN2NiMC0xZDMyLTRhN2EtYWYyNC0xNTA5Y2Q0NTg1NjEiLCJpYXQiOjE3OTI1NTczODUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhZGQtYWN0aW9uLWxhYmVsLnBuZyJ9.0kFn-8xNx_DWnaTSkzELx5OFFLoJoSOh45ZZlJRb3d4)
|
![after](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS81NmQ2YzY3Ny04YWI3LTQzMDktOTRiMC0wOTJkNTgzM2E2ODAiLCJpYXQiOjE3OTI1NTczODUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhZGQtcGx1Z2luLWxhYmVsLWRlc2t0b3AucG5nIn0._3owaAcwzwhfcTrnUDoHE6UXMr5yAVNyFwvutguZNtI)
|
![mobile](https://app.devin.ai/api/presigned_proxy?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpudWxsLCJidWNrZXRfbmFtZSI6ImRldmluYXR0YWNobWVudHMiLCJidWNrZXRfa2V5IjoiYXR0YWNobWVudHNfcHJpdmF0ZS9vcmdfT2NlNnlnR2RrdGJQekZsaS83NGI2MzFjNy00MTQ1LTRhODAtOWI4ZC1lMjU0NGFjYWM5NzgiLCJpYXQiOjE3OTI1NTczODUsIm9yZ19pZCI6Im9yZ19PY2U2eWdHZGt0YlB6RmxpIiwiZmlsZW5hbWUiOiJhZGQtcGx1Z2luLWxhYmVsLW1vYmlsZS5wbmcifQ.iLGvOSMna-jBeVFk5q2_Dboet95c4UpRL8cOecU_be4)
|

## How Has This Been Tested?

- Local ODS stack: opened Plugins > Add > OpenAPI plugin, checked
titles, button and toasts on desktop (1440px) and mobile (390px).
- `bunx jest` OpenAPI plugin modal, detail page and i18n suites: 101
passed.
- `pre-commit` (tsc, oxlint, oxfmt) on touched files.

## Additional Options

- [x] [Optional] Please cherry-pick this PR to the latest release
version.


Link to Devin session:
https://app.devin.ai/sessions/09df74c24e19407bbf0e7ab775b5cb0a
Open in Devin Desktop:
https://app.devin.ai/desktop/session/09df74c24e19407bbf0e7ab775b5cb0a?variant=devin
Requested by: @rohoswagger

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes the OpenAPI flow wording on the Plugins page, which previously
said "Add Action" despite living under Plugins. All user-facing copy —
modal titles, submit button, toasts, auth-modal text, tooltips, and the
Plugins "Add" menu — now says "OpenAPI plugin" / "Add Plugin", across
all 9 locales. Tests and the e2e helper were updated to match.

<sup>Written for commit be63963a3d.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16366?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/16366?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16366&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

- **User Interface**
- Renamed OpenAPI “actions” to “plugins” throughout creation, editing,
authentication, deletion, management, notifications, tooltips, and chat
settings.
- Updated the primary creation control from “Add Action” to “Add
Plugin.”
- Updated error messages and status notifications to use the new
terminology.

- **Localization**
- Applied the terminology updates across Arabic, Chinese, English,
French, German, Japanese, Korean, Portuguese, and Spanish translations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 17:06:12 +00:00
Wenxi f99f52681a feat(skills): add sharing controls to creation and management (#16318)
## Description

Expose sharing from the skill editor header and the overview card.
Support sharing settings during creation, saved with the new skill.
Organize the modal around who can access the skill, three compact
installation options, and ownership.

Show installation policies only to admins. Apply them to organization
access and invited users or groups. Keep ownership controls available
when the individual access section is hidden. Required skills remain
previewable and explain the admin requirement in a tooltip.

## How Has This Been Tested?

All 65 frontend tests passed across the sharing modal, editor, overview,
skill card, and shared card suites. TypeScript, formatting, lint, and
pre-commit checks passed. The complete stack also passed 238 backend
unit tests and 103 PostgreSQL-backed tests. No new live-browser run was
performed for this simplification.

## 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 skill sharing during creation and editing, with separate “can
use” and “can edit” permissions.
  * Added installation policies: Available, Default, and Required.
  * Added access, individual-access, people, and ownership controls.
* Required skills now display a status indicator and have restricted
deletion for non-admins.
  * Sharing is available directly from skill cards and the skills list.
* **Localization**
* Updated skill-sharing and installation-policy translations across
supported languages.
* **Bug Fixes**
* Improved sharing state handling, loading errors, refresh behavior, and
keyboard accessibility.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 04:59:06 +00:00
Wenxi 0db5771304 fix(opal): forward card DOM props and click handlers (#16354)
## Description

Forward native DOM props through the shared card component. Preserve its
visual and interaction state while allowing callers to open previews
through click handlers. This lets required skill cards remain
previewable when their toggle is unavailable.

## How Has This Been Tested?

The card regression tests passed within 65 frontend tests on the
complete stack. TypeScript, formatting, lint, and pre-commit checks
passed.

## 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**
* Cards now support standard HTML attributes, DOM events, and
accessibility attributes in plain and expandable modes.
  * Custom classes are merged with built-in card styling.
  * Internal expansion controls are kept out of the rendered element.

* **Documentation**
* Clarified attribute forwarding and interactive composition
requirements.
* Documented that styles are not forwarded and that callers provide
keyboard and button behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 04:59:05 +00:00
Raunak Bhagat 5159bd8287 feat(web): rework the cost-overrides panel and modal (#16324)
## Description

Reworks the Cost Overrides panel on the admin Language Models page.

- **Modal:** adding and editing happen in a "New / Edit Cost Override"
modal instead of an inline form. It runs on Formik with a Yup schema,
like the language-model provider modals: Save / Add stay disabled until
the form is valid and changed, and Enter submits.
- **Fields:** `InputHorizontal` rows with short titles and descriptions.
The three rates use `InputCurrency` (2 decimals). The edit path shows
the override's model in the model selector, disabled.
- **Cards:** each override is a `SelectCard` like the provider cards:
model icon, model name, provider and update date, the rates on a line
below, and trash / settings buttons beside the title. Delete now asks
for confirmation.
- **Search focus:** `InputSingleSelect` and `SimpleModelSelector` take a
`container`, passed to the `Dropdown`. The modal passes its content
element so the model search field keeps focus inside the dialog's focus
trap.
- Copy updated in all nine locales; unused placeholder keys removed.

UI only: it works with the current cost-override API. The data-model
change (overrides referencing `model_configuration`) comes in a separate
PR.

## Changes

### No overrides

<img width="867" height="203" alt="image"
src="https://github.com/user-attachments/assets/4ad5ee5a-27b9-49f1-b3e3-b7d0eb1140db"
/>

### >1 overrides

<img width="876" height="226" alt="image"
src="https://github.com/user-attachments/assets/0031f8cb-8f55-4d92-a809-c9fdf3f6e960"
/>

### Creating new override

<img width="655" height="420" alt="image"
src="https://github.com/user-attachments/assets/c700c36b-e099-4d98-b71e-855ff10f56e6"
/>

### Editing existing override

<img width="660" height="429" alt="image"
src="https://github.com/user-attachments/assets/121a09fc-5653-4435-894e-1c23cb002819"
/>


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Reworks the Cost Overrides panel on the admin Language Models page:
adding and editing now happen in a New/Edit Cost Override modal, and
each override renders as a card matching the provider cards above it. UI
only — the overrides API is unchanged.

**New Features**
- Add/edit moved from an inline form to a Formik + Yup modal; Add/Save
stays disabled until the form is valid and changed, and Enter submits.
Fields use `InputHorizontal` with short titles and unit descriptions.
- Rates use `InputCurrency` with unlimited precision, so sub-cent
negotiated rates ($0.075, $0.01875) save and display exactly; each card
lists In, Out, and (when set) Cache as separate items on a line under
the description. The edit path shows the override's model in a disabled
selector, or a read-only field when no configured model matches.
- Delete now asks for confirmation, disables itself while the request
runs, and shows success/error toasts; copy is updated in all nine
locales, unused placeholder keys were removed, and the API error path
now reuses `parseErrorDetail`.
- Tests that drove a mocked model picker were dropped: they didn't
exercise the real modal.

**Bug Fixes**
- `InputSingleSelect` and `SimpleModelSelector` accept a `container`
passed to the `Dropdown`, so the model search field keeps focus inside
the modal's focus trap.

<sup>Written for commit 7f24b81959.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16324?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/16324?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16324&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 modal-based forms for creating and editing cost overrides with
validation and model selection.
- Cost rates now support unlimited precision and display separate input,
output, and cache values alongside provider and update time.
- Added edit and confirmation-based delete actions with success and
error notifications.
  - Improved dropdown placement within dialogs and constrained layouts.

- **Localization**
- Updated cost-override labels, descriptions, units, titles, rate
details, and confirmation messages across supported languages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 03:20:53 +00:00
Raunak Bhagat 610809c564 feat(opal): type and paste into InputDatePicker like InputCurrency (#16364)
## Description

`InputDatePicker` now types and pastes like `InputCurrency`.

- Typing: a separator key (`/`, `-` or `.`) in the month or day splits
the segment at the caret. Digits before it stay and get padded (`1`
becomes `01`). Digits after it move to the front of the next segment,
and focus moves there. So `1/2/2026` types as it reads. Typing past a
full segment is blocked. Other non-digits do nothing.
- Pasting reads the text as a date in any segment:
- a whole date fills every segment: `10/19/2026`, `10-19-2026`,
`10.19.2026` or ISO `2026-10-19`;
- digits alone go into the focused segment at the caret, clipped to fit;
  - anything else (`10/19`, `Oct 19 2026`, `1/2/26`) is ignored.
- Arabic-Indic digits and invisible direction marks are handled as in
`InputCurrency`.
- A pasted date commits like a typed one, on blur or Enter.

`toAsciiDigits`, `cleanPastedText` and `usePendingCaret` move from
`InputCurrency` into `inputs/segmented.tsx`, so both fields share one
implementation. `InputTime` also reads Arabic-Indic digits now.

## Changes

TODO


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
`InputDatePicker` now types and pastes like `InputCurrency`, so a date
can be entered as it reads.

- Typing a separator (`/`, `-`, `.`) in the month or day splits the
segment at the caret: digits before it stay and get padded (`1` → `01`),
digits after it move to the front of the next segment, and focus moves
there, so `1/2/2026` fills all three segments. A separator in an empty
segment does nothing, keeping `10/19/2026` typing as it reads. Other
non-digits do nothing, and typing past a full segment is blocked.
- Pasting reads the text as a date in any segment: a whole date
(`10/19/2026`, `10-19-2026`, `10.19.2026`, or ISO `2026-10-19`) fills
every segment, digits alone go into the focused segment at the caret
clipped to fit, and anything else (`10/19`, `Oct 19 2026`, `1/2/26`) is
ignored. A pasted date commits like a typed one, on blur or Enter.
- Arabic-Indic digits and invisible direction marks are handled as in
`InputCurrency`.

The digit, paste-cleanup, and caret helpers move from `InputCurrency`
into `inputs/segmented.tsx` so both fields share one implementation; the
caret helper is now typed on the segments shape. `InputTime` also reads
Arabic-Indic digits now.

<sup>Written for commit ef69f89917.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16364?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/16364?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16364&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**
* Date inputs now support segment-based typing, separator handling, and
automatic movement between date fields.
* Dates can be pasted in supported formats, including `MM/DD/YYYY` and
`YYYY-MM-DD`.
* Arabic-Indic and Extended Arabic-Indic digits are recognized in date
and numeric inputs.
* Invalid, incomplete, or overly long input is handled consistently
without altering the field unexpectedly.
* Currency paste handling now provides improved whitespace, formatting,
and caret behavior.

* **Documentation**
* Added guidance on date formats, typing, pasting, validation, and
commit behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 02:56:10 +00:00
Raunak Bhagat d8a6fe50b6 feat(opal): add @opal/InputCurrency (#16323)
## Description

Adds `InputCurrency` to Opal: a segmented amount field that reads `$
[whole] . [fraction]` on the shared segmented-input chrome.

- `currency` (ISO 4217, default `USD`): the host locale sets the symbol,
its side (`3,00 €`) and the decimal separator, through a new
`OpalStrings.inputCurrencyParts`. `getCurrencyParts(locale, currency)`
is exported for the app's `OpalStringsBridge`.
- `precision` (default: the currency's usual decimals): the fraction
accepts at most this many digits; typing more is blocked. `0` hides the
fraction.
- The whole segment always resolves: leading zeros trim as soon as focus
leaves it (`0000` → `0`).
- Keyboard: `.` / `,` jump to the fraction, pasting `12.50` fills both
segments, arrows cross segment edges. Inside a form, Enter submits from
either segment; outside a form, Enter steps to the fraction.
- `onChange` fires on every edit (every draft is a valid amount); blur
and Enter tidy the display (`3.5` → `3.50`).
- New `inputs/numbers/` group: `InputCurrency` lives there and
`InputNumber` moves in beside it.
- New Opal strings for the field and segment labels, in all nine
locales.


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds `@opal/InputCurrency`, a segmented currency amount field that reads
`$ [whole] . [fraction]` on the shared segmented-input chrome, with the
symbol, its side, and the decimal separator driven by the host locale.

**New Features**

- Typing `.` or `,` jumps to the fraction and splits the amount at the
caret: an empty whole part fills `0` first (so `.5` reads `0.5`), digits
after the caret move to the front of the fraction clipped to the
precision, dropping any padded zeros (`1|234` on `1234.00` reads `1 .
234`), and with no fraction segment the separator does nothing. Other
non-digits, grouping keys included, do nothing. Pasting reads the text
as an amount: it drops whitespace, direction marks, grouping, and the
symbol or code, fills both segments (`12.50`) or inserts into the
focused segment, and ignores anything that is not an amount.
- `precision` caps the fraction digits (`0` hides the fraction,
`"unlimited"` and larger numbers clamp to 15, the most digits a JS
number holds exactly) and `currency` is typed as an ISO 4217
`CurrencyCode`; either change rebuilds the segments. Display rounds half
up from the shortest decimal form (0.075 shows 0.075, or 0.08 at
precision 2); leading zeros trim as soon as the whole segment loses
focus.
- `onChange` fires on every edit; blur and Enter tidy the display; Enter
submits a surrounding form from either segment.
- `getCurrencyParts(locale, currency)` is exported for
`OpalStringsBridge`, with new strings in all nine locales.
- `InputNumber` moves into a new `inputs/numbers/` group next to it.

<sup>Written for commit 72a6d133e5.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16323?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/16323?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16323&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 a locale-aware currency input with segmented editing,
configurable precision, validation states, keyboard navigation, RTL
support, and form integration.
- Added currency formatting utilities for regional symbols, separators,
placement, and decimal precision.
- Added public exports and Storybook examples for currency input
components.
  - Added active currency-code and precision type support.

- **Localization**
- Added accessibility labels for currency input parts in English,
Arabic, German, Spanish, French, Japanese, Korean, Portuguese, and
Chinese.

- **Documentation**
- Added currency input usage, behavior, props, and localization
guidance.
  - Updated number input documentation links and organization.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 02:27:31 +00:00
Raunak Bhagat a6d3b6bc05 feat(opal): move between segments with the arrow keys (#16322)
## Description

Segmented fields (`InputTime`, `InputDatePicker`) now move between
segments with the arrow keys. ArrowLeft at the start of a segment
focuses the previous segment, with the caret at its end. ArrowRight at
the end focuses the next segment, with the caret at its start.
Shift+Arrow and caret moves inside a segment stay native.

The change is in the shared `makeSegmentKeyDownHandler`
(`inputs/segmented.tsx`), which now takes an optional next-segment ref.
Both pickers pass it.

First PR of a stack: this → `InputCurrency` → cost-overrides modal.


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds arrow key navigation between segments in `InputTime` and
`InputDatePicker`. ArrowLeft at a segment's start moves focus to the
previous segment's end, and ArrowRight at the end moves to the next
segment's start.

Only a plain arrow with a collapsed caret crosses an edge;
Shift/Ctrl/Meta/Alt arrows and mid-segment moves stay native. Handled in
the shared `makeSegmentKeyDownHandler`, which now takes an optional
next-segment ref, and tests cover edge crossing and all guard cases.

<sup>Written for commit f3a28175b1.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16322?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/16322?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16322&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

- **Bug Fixes**
  - Improved keyboard navigation in segmented date and time inputs.
- Pressing **ArrowLeft** at the beginning of a segment now moves focus
to the previous segment.
- Pressing **ArrowRight** at the end of a segment now moves focus to the
next segment.
- Caret placement is preserved when moving between segments, while
selection and mid-segment navigation continue to work as expected.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-21 00:36:48 +00:00
devin-ai-integration[bot]andraunak b8c31c5b70 fix(web): clamp agents table cells and stop drag-reorder reshuffle (#16282)
## Description

These are fixes to the admin Agents table (`AgentsTable`), plus a new
opt-in column option in `@opal/Table`.

**1. Text overflow.** Long names and descriptions wrapped past the fixed
row height and overlapped the next row.

- The Name and Description cells now use `@opal/OverflowText` in place
of the legacy `refresh-components` `Text`. The text is clamped to 1
line. When the text is cut, hover it to see the full text in a tooltip.
- The `Content` cells now clamp to 1 line. Created By sets
`titleMaxLines={1}`. Access sets `titleMaxLines={1}` and
`descriptionMaxLines={1}`.
- `@opal/Table` data columns get a new option, `truncate?: boolean`.
When it is set, `TableCell` adds `w-0 min-w-full` to `.tbl-cell-inner`.
Under `table-layout: auto`, the content then cannot widen the column, so
long content truncates. Before this change, the Agents table grew to
2844px inside an 840px container. All Agents data columns set `truncate:
true`. The option is off by default, so other tables do not change. For
example, the Users `GroupsCell` measures its own width and needs the old
behavior.

**2. Random reshuffle after a drag-reorder.** The causes:

- `GET /api/admin/persona` (`get_persona_snapshots_for_user`) has no
`ORDER BY`, so the refetch after a save came back in arbitrary DB order.
- `handleReorder` sent only the moved range, as indexes into the visible
list. Other agents kept `null` or stale priorities, which collided with
the new values.
- The row snapped back until the refetch finished.

The fix:

- The table sorts agents client-side the way the paginated endpoint
does: `display_priority` ascending with nulls last, then `|id|`.
- `buildDisplayPriorityMap` merges the reordered visible IDs back into
the full agent list. It then sends a priority for every agent whose
position changed.
- The new priorities go into the SWR cache before the PATCH.
- `@opal/Table` now passes the `query`-filtered rows to
`useDraggableRows`, not all `items`. A drag while a search is active no
longer shifts hidden rows.

## How Has This Been Tested?

- `bun run types:check`, `oxlint`, `oxfmt` and the `@opal/Table` jest
tests pass.
- I tested in the browser on a local stack with 8 agents; see the
comment below. The checks were: no horizontal overflow at 840px and
1600px, ellipsis plus hover tooltip on long cells, and normal, far-below
and search-filtered drags. Each order persisted across reload and
matched the DB priorities.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.

Link to Devin session:
https://app.devin.ai/sessions/0d13205be9bb4560a7a0e55e880edd52
Open in Devin Desktop:
https://app.devin.ai/desktop/session/0d13205be9bb4560a7a0e55e880edd52?variant=devin

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes two bugs in the admin Agents table: long text no longer overlaps
the next row, and rows stay put after a drag-reorder.

- Data columns now accept an opt-in `truncate` flag; truncated columns
never widen the column, clamp content to one line, and show the full
value in a hover tooltip.
- The Name, Description, Created By, and Access cells clamp this way,
using `OverflowText` or one-line `Content` with max-line limits.
- Drag-reorder operates on query-visible rows only, so hidden rows keep
their positions.
- On drop, the full agent list is renumbered from the new visible order
and the priorities are written to the SWR cache before the PATCH, so
rows don't snap back while saving; nothing is saved when no priority
changed, and a failed save refreshes the table and shows an error.
- The table sorts agents client-side to match the server's order
(`display_priority` ascending with nulls last, then `|id|`).

<sup>Written for commit 32ab69a7c8.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16282?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/16282?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16282&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
-->
## Summary by CodeRabbit

* **New Features**
* Added text truncation for long agent names, descriptions, owners, and
access details in tables.
* Improved agent reordering, including consistent sorting and reliable
updates when filtered.
* **Bug Fixes**
* Preserved correct row positions during drag-and-drop when table
filters are active.
* **Documentation**
  * Updated table documentation to describe the new truncation option.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: raunak <raunak@onyx.app>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-20 17:37:10 +00:00
devin-ai-integration[bot] ecaa977d4d chore(web): remove unused useContentSize hook (#16304)
## Description

This PR removes `web/src/hooks/useContentSize.ts`. Its only caller was
`web/src/refresh-components/buttons/SelectButton.tsx`, and #16301
deleted that file. Now `git grep useContentSize` on `main` finds matches
only in the hook's own file. No barrel file re-exports the hook.

## How Has This Been Tested?

- `bun run types:check` passes with no type errors.
- The pre-commit hooks pass. These include type coverage, oxlint, oxfmt
and feature-map integrity.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.

Link to Devin session:
https://app.devin.ai/sessions/3efa311f9e28496fa1d729253e27b8d2
Open in Devin Desktop:
https://app.devin.ai/desktop/session/3efa311f9e28496fa1d729253e27b8d2?variant=devin

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Removes the unused `useContentSize` hook from
`web/src/hooks/useContentSize.ts`. Its only caller was
`SelectButton.tsx`, which was deleted in #16301, so `git grep
useContentSize` on `main` now finds matches only in the hook's own file,
and no barrel file re-exports it. No other code is affected.

<sup>Written for commit dcaa87ff56.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16304?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/16304?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16304&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
-->

## Summary by CodeRabbit

* **Refactor**
  * Removed an internal content-size measurement utility.
  * No direct user-facing feature changes are included in this update.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-20 02:48:31 +00:00
Wenxi b80667827f fix(plugins): manage default Chat selection in plugin settings (#16281)
## Description

Move MCP and OpenAPI default Chat settings out of Chat Preferences and
into Plugins. Chat Preferences keeps built-in controls and shows a
translated notice linking to Plugins.

OpenAPI Configuration now has an **Enable in default Chat** toggle.
Changes are saved with the form. Turning it off removes the plugin from
default Chat while preserving custom-agent use. OpenAPI tools remain
read-only; there are no per-operation switches. Admin directory status
reflects default Chat selection.

OpenAPI plugins no longer have a global disable control. Delete the
plugin to remove it from the workspace. Previously disabled records
remain off default Chat, but configured custom agents can use them,
subject to access and authentication. Explicit default Chat selection
normalizes the legacy flag. This uses existing fields and associations,
with no migration. New full-admin setup defaults the Chat toggle on;
scoped creators cannot change workspace Chat defaults.

MCP retains its server-level default Chat switch and global per-tool
controls. Removing the Chat Preferences cards intentionally removes
individual MCP tool selection specifically for default Chat. OpenAPI
retains its whole-plugin default Chat choice in Configuration.

Terraform `onyx_custom_tool.enabled` now controls default Chat
selection. The provider reads and writes `default_chat_enabled` through
the custom-tool API. Metadata and selection save atomically, parallel
resources preserve unrelated selections, and omitted OAuth settings
remain unchanged. Upgrade the provider alongside the server; older
versions use the removed global status writes. No Terraform state schema
migration is required.

Update the feature map and simplify the OpenAPI tools list to shared
name/description rows with consistent search and counts. Rows retain
composite method/path/name keys.

## How Has This Been Tested?

- 149 frontend tests passed across plugin forms, member details, and the
admin directory.
- Backend availability unit tests passed, including permission checks,
legacy selections, and omitted versus explicitly cleared OAuth settings.
Additional MCP/default Chat unit checks passed.
- 13 PostgreSQL tests passed against a disposable ODS database. Coverage
includes legacy OpenAPI construction, atomic API create/update/read,
rollback, and concurrent selections.
- Terraform provider `go test ./...` passed and generated documentation
is current. Live Terraform acceptance tests were not run.
- Repository pre-commit checks passed, including type checks,
formatting, lint, translations, and feature-map integrity. `git diff
--check` passed.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added per-plugin availability controls for Default Chat, including MCP
servers and OpenAPI actions.
* OpenAPI actions disabled for Default Chat remain available to
configured custom agents.
* Added permission-aware controls so only workspace administrators can
change Default Chat availability.
* Added support for managing these settings through Terraform custom
tools.

* **Improvements**
* Chat Preferences now focuses on built-in tools, with links directing
administrators to Plugins for MCP and OpenAPI configuration.
  * Improved OpenAPI action search and display.

* **Localization**
  * Added translated availability guidance across supported languages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-20 02:32:28 +00:00
Wenxi e1dda7a89e feat(plugins): align directory ordering and availability indicators (#16273)
## Description

Align admin and member plugin directory ordering and availability
indicators. Member indicators show Main Chat, custom agents, and Craft
in clockwise order, with one gray dot when workspace setup is required.
Unsupported surfaces appear in the tooltip.

Include default Chat availability in the Craft MCP catalog so sorting
and indicators use known state. Keep indicator geometry consistent in
RTL locales.

## How Has This Been Tested?

31 member directory tests and 11 backend availability tests passed.
Type, lint, formatting, and feature-map checks passed.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Plugin directories show separate availability indicators for Chat,
custom agents, and Craft, with detailed status tooltips.
- Plugin cards are sorted by availability, type, and locale-aware name.
- Availability labels for disabled, unavailable, custom-agent, and
unknown states are translated across supported languages.
- Craft MCP listings indicate whether each server is available in
default Chat.

- **Bug Fixes**
- Indicators and sorting more accurately reflect disabled skills,
connection issues, unsupported surfaces, and Craft availability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-20 02:32:28 +00:00
Wenxi 26e288019a feat(plugins): unify member connection and settings modals (#16272)
## Description

Unify member plugin connection, tools, approvals, and skill settings in
persistent modals. Availability and skill changes remain drafts until
Save, and previews reuse the existing modal.

Keep the backdrop and active content in one modal root so long content
scrolls through connection transitions.

Show connection and skill issues in separate cards. Skill-name conflicts
now offer explicit replacement confirmation without discarding other
edits.

## How Has This Been Tested?

87 directory, detail, and connection tests plus 7 skill-panel tests
passed. Coverage includes conflict confirmation, draft retention, stable
backdrop identity, native wheel scrolling, and outside-click behavior.
The scrolling regression fails against the old nested modal roots. Type,
lint, formatting, translation, and feature-map checks passed.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Plugin details now group tool availability and approval settings under
Tools, with editable preferences saved or discarded together.
* Craft app skill settings support previews, draft enablement changes,
save/cancel controls, and conflict handling when enabling a skill
requires replacement.
* Connection details show readiness, setup issues, connect/disconnect
controls, and warnings when associated skills are disabled.
* Plugin setup guidance clarifies availability across default Chat,
Craft sessions, and scheduled tasks, including when custom agents may
still use a plugin.
* **Documentation**
  * Updated plugin guidance and terminology across supported languages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-20 02:32:27 +00:00
Wenxi 7fbe49a24b feat(plugins): unify admin plugin configuration forms (#16271)
## Description

Unify admin MCP, OpenAPI, and Craft App configuration forms.
Availability changes remain drafts until Save. MCP access and
availability controls appear on the first setup page; authentication
stays on the second.

OpenAPI Configuration combines schema, optional display description,
schema-backed agent description, and workspace availability. Creation
keeps one modal backdrop across steps.

Preserve automatic OAuth setup after authentication and protect
refreshed credentials when saving setup metadata. Unrelated saves
preserve other administrators’ availability changes. Pending Chat
enablement can retry discovery without changing authentication.

## How Has This Been Tested?

Targeted MCP, OpenAPI, and Craft App component suites passed, including
draft retention, access checks, retry behavior, concurrent availability
edits, and implicit form submission. OAuth backend regressions passed,
including concurrent token refresh preservation. Type, lint, formatting,
translation, and feature-map checks passed.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Configure app availability for Chat, Craft, and the workspace, with
changes applied when saved.
* Choose Chat and Craft availability during MCP setup; OAuth setup
choices are remembered.
* Edit OpenAPI schema, descriptions, and availability together in
Configuration.
  * Create apps and tools as enabled or disabled.

* **Changes**
* Availability indicators are read-only; manage settings in
configuration.
* MCP approval settings are separate from server configuration, and tool
counts include locked tools.
* MCP setup validates access before connection; unchanged credentials
cannot be resubmitted unnecessarily.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-20 02:32:27 +00:00
devin-ai-integration[bot] 9e65247dc6 chore(web): remove unused refresh-components SelectButton (#16301)
## Description

#16242 moved `sections/knowledge/SourceHierarchyBrowser.tsx` to Opal
`SelectButton`. That file was the last caller of
`@/refresh-components/buttons/SelectButton`, so nothing imports the
refresh component now. The only references left were its own story and
lint config.

This PR removes:
- `web/src/refresh-components/buttons/SelectButton.tsx` and
`SelectButton.stories.tsx`.
- Its two baseline entries in `web/.oxlintrc.json`.
- Its row in
`web/tools/oxlint/opal/rules/no-replaced-refresh-import.ts`. The rule's
comment says to delete the row when the refresh module is deleted.
- The `src/refresh-components/buttons` floor in
`web/.type-coverage-baseline.yaml`, lowered from 99.7 to 99.6. The
deleted file was fully typed, so the directory ratio fell by a rounding
step.

## How Has This Been Tested?

- `bun run types:check`: no type errors.
- `bunx oxlint`: no new errors.
- Pre-commit hooks (oxfmt, oxlint, type coverage, feature-map integrity)
pass.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.

Link to Devin session:
https://app.devin.ai/sessions/ba1242cc5338467c8db2fdfa02b81d10
Open in Devin Desktop:
https://app.devin.ai/desktop/session/ba1242cc5338467c8db2fdfa02b81d10?variant=devin

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Removes the now-unused `SelectButton` from `refresh-components` after
#16242 moved the last caller to Opal's component.

- Deletes `SelectButton.tsx` and its story file, along with its lint
baseline entries.
- Removes the `SelectButton` row from the `no-replaced-refresh-import`
rule.
- Lowers the type coverage baseline for `src/refresh-components/buttons`
from 99.7 to 99.6, since the removed file was fully typed.

<sup>Written for commit 6856447fb6.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16301?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/16301?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16301&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
-->

## Summary by CodeRabbit

* **Removed**
  * Removed the SelectButton component and its Storybook examples.
* Removed support for tracking SelectButton as a design-system and
layout baseline item.
  * Removed related replacement guidance for SelectButton imports.

* **Maintenance**
  * Adjusted the type-coverage threshold for refresh button components.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-20 01:38:55 +00:00
Raunak Bhagat 0fe91853b1 feat(opal): stored-secret password tag, tab keyboard nav, tertiary actions (#16275)
## Description

**1. Stored secrets in `InputPasswordTypeIn`**
- New prop `existingValue`: a stored value from the backend, already
masked.
- While `value === existingValue`:
- the field shows the value as a medium blue `Tag` with the key icon,
and the input itself stays empty;
  - the reveal toggle is disabled;
  - Backspace or Delete clears the value;
  - typing or pasting replaces the value with the new text.
- After the first edit, the field is a normal password field again.
- To keep the stored secret on submit, the caller compares the submitted
value with `existingValue`.
- The tag has no tooltip.
- An unchanged existing value does not block submission: the native
`required` check is off while the tag shows. A `readOnly` field keeps
its existing value, and the caller's `onKeyDown` runs first and can
cancel the deletion.
- The `mask` prop and its ✱ overlay are removed. Password fields always
use the browser's own dots.

**Callers that edit a stored secret now pass `existingValue`**
- LLM provider modals (`APIKeyField`, Bedrock, Ollama), SSO provider
modal, index settings, web search, voice, tracing, image generation
(OpenAI, Azure), OpenAPI OAuth client secret, MCP API token and OAuth
secret, MCP user credentials, Craft app user credentials, Slack bot
tokens.
- In each one, the stored masked string is the field's initial value. So
the untouched field keeps Formik's `dirty` false, and a Save gated on
`dirty` stays blocked. Submit logic does not change: an unchanged value
still counts as unchanged.
- SSO used `isNonRevealable` on edit only to protect the stored value.
It uses `existingValue` now.
- No change where the form starts empty for a stored secret: the hooks
modal (only a placeholder holds the masked key), the Discord bot config
and the custom-analytics key.

**Raw password inputs move to `InputPasswordTypeIn`**
- Slack bot tokens, reset password, impersonate (API key,
`autoComplete="new-password"`), custom analytics secret key, plugin
connection dialog.

**2. Keyboard and tab navigation (from the closed #16238)**
These are the Opal parts of the closed Plugins PR. The plugins-specific
parts are left out.
- `Tabs`:
- new `activationMode`. In `"manual"`, the default, arrows move focus
and Enter or Space selects.
  - a focus ring shows while the keyboard moves focus;
- Tab enters the list on the selected tab and the next Tab leaves it, as
the ARIA tabs pattern says;
- Escape leaves the tabs, but only from a tab: a control in
`rightChildren` keeps focus;
  - only a left click focuses a tab.
- `orientation` is omitted from the props: Opal tabs are horizontal
only.
- `Interactive` (stateless): keyboard focus shows the hover state in
place of the browser's ring.
- `useGridNavigation`: keyboard focus on a control inside a grid item
shows again.
- `InputTypeIn` clear button: it keeps focus in the field after it
clears the value, it has an `aria-label`, and it sends `onChange` a real
change event.

- Escape on a control beside a text field (the clear button, the reveal
toggle, a chevron, a chip) returns focus to the field, also when a
tooltip consumed the key. This covers `InputTypeIn` and every input
built on it, plus `TagField`. On `InputTime` and `InputDatePicker`,
which have no single input, it focuses the first segment.

**3. Input action buttons**
- The right-side icon buttons use `tertiary` prominence: the clear
buttons of `InputTypeIn`, `TagField`, `InputTime` and `InputDatePicker`,
and the date picker's calendar button.
- The `InputTypeIn` and `UploadControl` clear buttons have tooltips.
- The tooltip-only clear and calendar buttons of `TagField`, `InputTime`
and `InputDatePicker` get an `aria-label`, so they have an accessible
name.
- The clear button is always an input's last control. `InputDatePicker`
drew it before the calendar button, and `TagField` (`InputTypeInTag`,
the multi-select) drew it before the dropdown chevron.

- `TagField` (`InputTypeInTag`, the multi-select) uses `size="sm"` for
its clear button, as the other inputs do. It used `xs`.

## Additional Options

- [ ] [Optional] Please cherry-pick this PR to the latest release
version.
- [x] [Optional] Override Linear Check

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds an `existingValue` prop to `InputPasswordTypeIn` so forms that edit
backend secrets show the saved value as a key tag instead of editable
masked text, and polishes Opal tab keyboard navigation, focus handling,
and input action buttons.

**Stored secrets**
- `existingValue` marks a stored backend value: while the field's value
equals it, the value renders as a key tag, the input stays empty, and
the reveal toggle is disabled.
- Backspace or Delete clears it; typing or pasting replaces it whole. A
read-only field keeps the value, the caller's `onKeyDown` can cancel the
deletion, and an unchanged tag doesn't fail a native required check.
- Secret-editing forms (Slack tokens, SSO, MCP, language-model and
image-gen modals, tracing, voice, web search, and more) now pass
`existingValue`; a few raw password inputs move to
`InputPasswordTypeIn`. The `mask` prop and idle asterisk overlay are
removed.
- E2E specs for OpenAPI OAuth, index settings, and web search now verify
the stored value appears as a tag with an empty input.

**Keyboard and input actions**
- `Tabs` defaults to manual activation: arrows move focus, Enter or
Space selects, a focus ring tracks keyboard movement, and Escape leaves
the tabs. Only a plain left click focuses a tab; a caller's `onKeyDown`
can cancel the Escape.
- Keyboard focus on `Interactive` surfaces replaces the browser ring
with the hover state; `useGridNavigation` now shows focus on controls
inside grid items.
- `InputTypeIn`'s clear button sends a real change event, keeps focus in
the field after clearing, and gains an `aria-label`.
- Escape on an input's side controls (clear, reveal toggle, chevron,
chip) returns focus to the field, even when a tooltip consumed the key;
on `InputTime` and `InputDatePicker` it focuses the first segment.
- Input action buttons use `tertiary` prominence, the clear button is
always an input's last control, and `TagField`'s clear button is `sm`
like the other inputs'.

<sup>Written for commit 66ab5fd5b3.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16275?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/16275?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16275&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
-->
## Summary by CodeRabbit

* **New Features**
* Password fields display saved credentials as masked, non-revealable
tags. Clear them with Backspace or Delete, or replace them by typing or
pasting.
* Tabs use manual activation: arrow keys move focus, while Enter or
Space selects.

* **Improvements**
* Credential fields preserve saved values while editing across
administrative and configuration forms.
* Clear controls provide localized labels and tooltips, and return focus
to the field.
  * Escape restores focus from date, time, tag, and input controls.
* Keyboard focus styling is more consistent across interactive controls
and grid navigation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-20 01:32:59 +00:00