◐ Shell
clean mode source ↗

feat(site/src): add advisor admin settings UI by ThomasK33 · Pull Request #24624 · coder/coder

This was referenced

Apr 22, 2026

@ThomasK33 ThomasK33 changed the base branch from feat/advisor-03-config-api to graphite-base/24624

April 23, 2026 06:32

chatgpt-codex-connector[bot]

@ThomasK33 ThomasK33 changed the base branch from graphite-base/24624 to feat/advisor-03-config-api

April 23, 2026 07:02

@ThomasK33 ThomasK33 changed the title feat(site/src/pages/AgentsPage): add advisor admin settings UI feat(site/src): add advisor admin settings UI

Apr 23, 2026

chatgpt-codex-connector[bot]

coder-agents-review[bot]

ibetitsmike

@ThomasK33 ThomasK33 changed the base branch from feat/advisor-03-config-api to graphite-base/24624

April 30, 2026 12:23

@graphite-app graphite-app Bot changed the base branch from graphite-base/24624 to main

April 30, 2026 12:54
Adds an admin-only "Advisor" section to the Agent Settings →
Behavior page, following the Virtual Desktop pattern.

- API client methods and TanStack Query hooks for the advisor
  config endpoint, with automatic cache invalidation on save.
- AdvisorSettings component: toggle + max uses per run (0 =
  unlimited) + max output tokens + reasoning effort dropdown +
  advisor model override dropdown.
- Storybook stories covering enabled/disabled/saving/error.
- Wires the section into AgentSettingsBehaviorPage + its view,
  with updated page and page-view stories.

Uses generated AdvisorConfig / UpdateAdvisorConfigRequest types
from codersdk. Handles the zero UUID as the "use chat model"
sentinel for both reads and writes.

Signed-off-by: Thomas Kosiewski <tk@coder.com>

---
_Generated with `mux`_
…rrides fail to load

Change-Id: I521233cf90e70aea4876b6686bae629bfed234e0
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ia63f9839f981234f106356073b0fb6040e5ce16a
Signed-off-by: Thomas Kosiewski <tk@coder.com>
- Add missing isAdvisorConfigFetching prop default in parent stories to
  unblock the tsc lint CI check (DEREM-25).
- Show "Loading..." instead of the "Unavailable model" fallback on the
  advisor model trigger while model configs are still being fetched
  (DEREM-15).
- Skip numeric/reasoning validation when the advisor feature is
  disabled so admins can persist the disable action even when hidden
  fields are in an invalid state.
- Add Storybook stories for the refetching, loading-model-configs, and
  model-configs-error branches (DEREM-26, DEREM-27).
…display name is blank

Model configs may have an empty display_name because the backend trims
without enforcing a non-empty fallback. Use the same
display_name.trim() || model pattern already used by
ExploreModelOverrideSettings for both the option list and the selected
trigger label so admins never see a blank advisor model entry.

Change-Id: I870a2a97757e2af1728dc1aa45686cd932b777fe
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Preserve stored advisor limits when saving with the advisor disabled so
that hidden, invalid mid-edit values cannot silently overwrite the
server-side configuration on re-enable. Surface backend error detail in
the inline save error via getErrorMessage so admins can distinguish
between the advisor config's validation failure modes. Add stories that
exercise the enable-then-disable-then-save path and the detailed save
error rendering.

Change-Id: I31eabc40b604e7b18a840b348197a01da097f5f6
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Track the most recently committed advisor values via a ref so the
disable-path payload no longer reads from the potentially stale
advisorConfigData query snapshot. This prevents recently saved limits
and model selections from being silently rolled back when the user
disables the advisor before a refetch completes or when the refetch
fails. Add a story that exercises deselecting a real advisor model back
to the chat-model fallback so the reverse direction of the model
dropdown is covered by the play function suite.

Change-Id: I5f3c1d9ad4f2b1c7e9b8a5c2d1e0f6a4b3c2d1e0
Signed-off-by: Thomas Kosiewski <tk@coder.com>
… disable

Clearing the model override when disabling prevents the save from
failing with a 400 when the previously stored model config has been
deleted. Adds stories covering the disable-with-deleted-model path and
client-side validation blocking Save on dirty-but-invalid input.

Change-Id: I988bc240f102841f1428f8b940ce5d979fcb05dd
Signed-off-by: Thomas Kosiewski <tk@coder.com>
…en when model configs fail to load

When an admin disables the advisor and the saved model override
references a model that no longer exists, the backend rejects the
stale ID with a 400. The previous guard only cleared the override
when `modelConfigsError` was falsy, so a failing model-configs query
left the admin unable to disable the advisor. Drop the
`!modelConfigsError` check so the override is scrubbed whenever the
current list (cached or freshly fetched) does not contain the stored
ID. Add `DisableAdvisorWithDeletedModelAndModelConfigsError` to
prove the payload carries `model_config_id: nilUUID` in this edge
case.

Change-Id: Ie0e8e34a45351f3b87e1a4cacd9e127c6f15e543
Signed-off-by: Thomas Kosiewski <tk@coder.com>
…ring loading

Previously, the disable guard skipped stale-ID cleanup while the
model-configs query was still loading, so racing a disable against
the in-flight fetch forwarded a deleted model_config_id and the
backend rejected the request with a 400. Drop the loading guard so
disable stays reliable in that race; the rationale for clearing when
nothing can be verified in the cache already applied to the failing
fetch case.

Change-Id: I7284d62df7f3372dc5f20fa58b214e926941f682
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Addresses DEREM-32 and DEREM-33 from the round 11 agent review.

DEREM-32 (P0): Formik's `handleChange` parses `type="number"` inputs
with `parseFloat`, which replaced the declared `string` form value
with a `number` at runtime. `isNonNegativeIntegerString` then threw on
`.trim()` for every numeric keystroke, freezing `form.errors` and
pinning Save as disabled. Write the raw `event.currentTarget.value`
string via `setFieldValue` on both numeric inputs to keep the runtime
value aligned with `AdvisorSettingsFormValues`.

DEREM-33 (P3): The stale-model scrub on disable also fired during
initial page load when `modelConfigs` was still `[]`, silently clearing
a valid override when the admin disabled before the query settled.
Narrow the scrub to only run when model configs have loaded
successfully and the list is non-empty, so an in-flight or failing
fetch forwards the override unchanged and the backend can surface a
specific 400 if the ID really has been deleted. Rename and rework the
two stories that previously asserted the aggressive scrub to instead
assert the override is preserved during load and on error.

Signed-off-by: Thomas Kosiewski <tk@coder.com>
…en model list is empty

Disabling the advisor with a deleted model_config_id and zero model
configs would forward the stale UUID and fail the save with a 400.
The loading and error guards already cover the "not loaded yet" case,
so an empty list after a successful load is a definitive answer that
the override is missing. Add a story that locks in the recovery path.

Change-Id: I2be12c3228419c6cd62e4979bdc4d0d29c38102f
Signed-off-by: Thomas Kosiewski <tk@coder.com>
react-query keeps `isLoading` false during background refetches when
cached data exists, so the disable-path scrub could decide from stale
model data and either preserve a now-deleted override (causing a 400 on
save) or silently drop a still-valid override missing from a stale
cache. Gate the scrub on `isFetching` as well so refetches are treated
as indeterminate.

Change-Id: I7123032f2671c6e0ba454835b521d4c0e57c452d
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>

@ThomasK33 ThomasK33 deleted the feat/advisor-06-admin-settings-ui branch

April 30, 2026 13:41