feat(webui): expand and organize settings with autosave #9144
ci.yml
on: pull_request
Detect changes
7s
Python (Windows process compatibility)
1m 58s
webui
1m 16s
docker
1m 19s
Matrix: test
Matrix: tui
Annotations
7 errors and 14 warnings
|
webui
Process completed with exit code 1.
|
|
src/tests/settings-providers.test.tsx > Settings providers > recognizes and removes versioned web search tools without losing raw settings:
webui/src/tests/settings-providers.test.tsx#L693
TestingLibraryElementError: Unable to find role="button" and name `/^OpenAI https:/`
Ignored nodes: comments, script, style
<body>
<div>
<div
class="flex min-h-0 flex-1 flex-col overflow-hidden bg-settings-canvas lg:flex-row"
>
<aside
class="flex w-full shrink-0 flex-col bg-settings-surface px-3 pb-2 lg:w-[17rem] lg:px-3 lg:pb-4 pt-4 lg:pt-4"
>
<button
aria-label="Back to chat"
class="touch-target mb-2 inline-flex h-9 w-9 items-center justify-center rounded-full text-[13px] font-medium text-muted-foreground transition-colors hover:bg-muted/70 hover:text-foreground lg:mb-3"
type="button"
>
<svg
aria-hidden="true"
class="lucide lucide-chevron-left h-[1em] w-[1em]"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m15 18-6-6 6-6"
/>
</svg>
</button>
<nav
aria-label="Settings sections"
class="w-full"
>
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="Settings: Models"
class="touch-target flex h-11 w-full items-center gap-2.5 rounded-control bg-sidebar-accent px-3 text-left text-[13px] font-medium text-foreground transition-colors hover:bg-sidebar-accent/80 lg:hidden"
data-state="closed"
id="radix-:r48:"
type="button"
>
<svg
aria-hidden="true"
class="lucide lucide-sliders-horizontal h-[1em] w-[1em] shrink-0"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="21"
x2="14"
y1="4"
y2="4"
/>
<line
x1="10"
x2="3"
y1="4"
y2="4"
/>
<line
x1="21"
x2="12"
y1="12"
y2="12"
/>
<line
x1="8"
x2="3"
y1="12"
y2="12"
/>
<line
x1="21"
x2="16"
y1="20"
y2="20"
/>
<line
x1="12"
x2="3"
y1="20"
y2="20"
/>
<line
x1="14"
x2="14"
y1="2"
y2="6"
/>
<line
x1="8"
x2="8"
y1="10"
y2="14"
/>
<line
x1="16"
x2="16"
y1="18"
y2="22"
/>
</svg>
<span
class="min-w-0 flex-1 truncate"
>
Models
</span>
<svg
aria-hidden="true"
class="lucide lucide-chevron-down h-[1em] w-[1em] shrink-0 text-muted-foreground"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m6 9 6 6 6-6"
/>
</svg>
</button>
<div
class="relativ
|
|
src/tests/settings-providers.test.tsx > Settings providers > maps provider request switches to raw extraBody fields:
webui/src/tests/settings-providers.test.tsx#L622
TestingLibraryElementError: Unable to find an accessible element with the role "button" and name `/^OpenAI https:/`
Here are the accessible roles:
complementary:
Name "":
<aside
class="flex w-full shrink-0 flex-col bg-settings-surface px-3 pb-2 lg:w-[17rem] lg:px-3 lg:pb-4 pt-4 lg:pt-4"
/>
--------------------------------------------------
button:
Name "Back to chat":
<button
aria-label="Back to chat"
class="touch-target mb-2 inline-flex h-9 w-9 items-center justify-center rounded-full text-[13px] font-medium text-muted-foreground transition-colors hover:bg-muted/70 hover:text-foreground lg:mb-3"
type="button"
/>
Name "Settings: Models":
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="Settings: Models"
class="touch-target flex h-11 w-full items-center gap-2.5 rounded-control bg-sidebar-accent px-3 text-left text-[13px] font-medium text-foreground transition-colors hover:bg-sidebar-accent/80 lg:hidden"
data-state="closed"
id="radix-:r3f:"
type="button"
/>
Name "Overview":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Appearance":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Models":
<button
aria-current="page"
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-sidebar-accent-foreground"
type="button"
/>
Name "Image":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Voice":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Web":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Channels":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Memory":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "System":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Advanced":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transiti
|
|
src/tests/i18n.test.tsx > webui i18n > keeps every locale aligned with the English resource shape:
webui/src/tests/i18n.test.tsx#L592
AssertionError: expected { locale: 'zh-TW', …(3) } to deeply equal { locale: 'zh-TW', missing: [], …(2) }
- Expected
+ Received
Object {
"extra": Array [],
"interpolationMismatches": Array [],
"locale": "zh-TW",
- "missing": Array [],
+ "missing": Array [
+ "settings.runtimeConfig.apiSaved",
+ "settings.runtimeConfig.fields.agents_defaults_timezone_mode.label",
+ "settings.runtimeConfig.fields.agents_defaults_timezone_mode.help",
+ "settings.runtimeConfig.fields.agents_defaults_timezone.label",
+ "settings.runtimeConfig.fields.agents_defaults_timezone.help",
+ "settings.runtimeConfig.fields.agents_defaults_dream_enabled.label",
+ "settings.runtimeConfig.fields.agents_defaults_dream_enabled.help",
+ "settings.runtimeConfig.fields.gateway_heartbeat_enabled.label",
+ "settings.runtimeConfig.fields.gateway_heartbeat_enabled.help",
+ "settings.runtimeConfig.fields.gateway_heartbeat_interval_s.label",
+ "settings.runtimeConfig.fields.gateway_heartbeat_interval_s.help",
+ "settings.runtimeConfig.fields.agents_defaults_max_tool_iterations.label",
+ "settings.runtimeConfig.fields.agents_defaults_max_tool_iterations.help",
+ "settings.runtimeConfig.fields.agents_defaults_max_concurrent_subagents.label",
+ "settings.runtimeConfig.fields.agents_defaults_max_concurrent_subagents.help",
+ "settings.runtimeConfig.fields.agents_defaults_max_tool_result_chars.label",
+ "settings.runtimeConfig.fields.agents_defaults_max_tool_result_chars.help",
+ "settings.runtimeConfig.fields.agents_defaults_provider_retry_mode.label",
+ "settings.runtimeConfig.fields.agents_defaults_provider_retry_mode.help",
+ "settings.runtimeConfig.fields.agents_defaults_tool_hint_max_length.label",
+ "settings.runtimeConfig.fields.agents_defaults_tool_hint_max_length.help",
+ "settings.runtimeConfig.fields.tools_max_session_messages_per_minute.label",
+ "settings.runtimeConfig.fields.tools_max_session_messages_per_minute.help",
+ "settings.runtimeConfig.fields.agents_defaults_unified_session.label",
+ "settings.runtimeConfig.fields.agents_defaults_unified_session.help",
+ "settings.runtimeConfig.fields.tools_exec_enable.label",
+ "settings.runtimeConfig.fields.tools_exec_enable.help",
+ "settings.runtimeConfig.fields.tools_file_enable.label",
+ "settings.runtimeConfig.fields.tools_file_enable.help",
+ "settings.runtimeConfig.fields.tools_web_enable.label",
+ "settings.runtimeConfig.fields.tools_web_enable.help",
+ "settings.runtimeConfig.fields.tools_cli_apps_enable.label",
+ "settings.runtimeConfig.fields.tools_cli_apps_enable.help",
+ "settings.runtimeConfig.fields.tools_my_enable.label",
+ "settings.runtimeConfig.fields.tools_my_enable.help",
+ "settings.runtimeConfig.fields.tools_my_allow_set.label",
+ "settings.runtimeConfig.fields.tools_my_allow_set.help",
+ "settings.runtimeConfig.fields.tools_exec_timeout.label",
+ "settings.runtimeConfig.fields.tools_exec_timeout.help",
+ "settings.runtimeConfig.fields.tools_exec_path_prepend.label",
+ "settings.runtimeConfig.fields.tools_exec_path_prepend.help",
+ "settings.runtimeConfig.fields.tools_exec_path_append.label",
+ "settings.runtimeConfig.fields.tools_exec_path_append.help",
+ "settings.runtimeConfig.fields.tools_exec_sandbox.label",
+ "settings.runtimeConfig.fields.tools_exec_sandbox.help",
+ "settings.runtimeConfig.fields.tools_exec_sandbox_ro_binds.label",
+ "settings.runtimeConfig.fields.tools_exec_sandbox_ro_binds.help",
+ "settings.runtimeConfig.fields.tools_exec_sandbox_rw_binds.label",
+ "settings.runtimeConfig.fields.tools_exec_sandbox_rw_binds.help",
+ "settings.runtimeConfig.fields.tools_exec_allowed_env_keys.label",
+ "settings.runtimeConfig.fields.tools_exec_allowed_env_keys.help",
+ "settings.runtimeConfig.fields.tools_exec_allow_patterns.label",
+ "settings.runtimeConfig.fields.tools_exec_allow_patterns.help",
+ "settings.runtimeConfig.fields.tools_exec_deny_pa
|
|
src/tests/app-layout.test.tsx > App layout > opens the settings view from the sidebar footer:
webui/src/tests/app-layout.test.tsx#L2581
TestingLibraryElementError: Unable to find an accessible element with the role "button" and name "Security"
Here are the accessible roles:
button:
Name "Settings: Overview":
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="Settings: Overview"
class="touch-target flex h-11 w-full items-center gap-2.5 rounded-control bg-sidebar-accent px-3 text-left text-[13px] font-medium text-foreground transition-colors hover:bg-sidebar-accent/80 lg:hidden"
data-state="closed"
id="radix-:r19n:"
type="button"
/>
--------------------------------------------------button:
Name "Overview":
<button
aria-current="page"
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-sidebar-accent-foreground"
type="button"
/>
Name "Appearance":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Models":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Image":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Voice":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Web":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Channels":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Memory":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "System":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
Name "Advanced":
<button
class="touch-target flex h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-[13px] font-medium relative z-[1] transition-[color] duration-150 ease-out motion-reduce:transition-none text-muted-foreground/78 hover:bg-muted/45 hover:text-foreground"
type="button"
/>
--------------------------------------------------
Ignored nodes: comments, script, style
<nav
aria-label="Settings sections"
class="w-full"
>
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="Settings: Overview"
class="touch-target flex h-11 w-full items-center gap-2.5 rounded-control bg-sidebar-accent px
|
|
src/tests/app-layout.test.tsx > App layout > uses one main landmark and a page heading in desktop settings:
webui/src/tests/app-layout.test.tsx#L488
TestingLibraryElementError: Unable to find an accessible element with the role "heading" and name "Settings"
Here are the accessible roles:
main:
Name "":
<main
class="relative flex h-full min-w-0 flex-1 flex-col overflow-hidden bg-background"
/>
--------------------------------------------------
region:
Name "Conversation workbench":
<section
aria-label="Conversation workbench"
class="flex h-full min-h-0 flex-col overflow-hidden bg-background"
/>
Name "nanobot":
<section
aria-label="nanobot"
class="workbench-pane relative flex min-h-0 min-w-0 overflow-hidden bg-background"
data-active="true"
data-testid="workbench-pane-new-topic"
/>
--------------------------------------------------
button:
Name "Toggle sidebar":
<button
aria-label="Toggle sidebar"
class="inline-flex items-center justify-center whitespace-nowrap rounded-control text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 h-7 w-7 rounded-md text-muted-foreground hover:bg-accent/35 hover:text-foreground lg:hidden"
/>
Name "Temporary chat":
<button
aria-label="Temporary chat"
aria-pressed="false"
class="inline-flex items-center justify-center whitespace-nowrap rounded-control text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 host-no-drag h-8 w-8 shrink-0 rounded-full bg-transparent text-muted-foreground shadow-none transition-none hover:text-foreground hover:bg-accent/45"
data-state="closed"
type="button"
/>
Name "Toggle theme from header":
<button
aria-label="Toggle theme from header"
class="inline-flex items-center justify-center whitespace-nowrap rounded-control text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 host-no-drag h-8 w-8 rounded-full text-muted-foreground/85 hover:bg-accent/40 hover:text-foreground"
/>
Name "Attach files":
<button
aria-label="Attach files"
class="inline-flex items-center justify-center whitespace-nowrap rounded-control text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 thread-composer-action touch-target rounded-full text-muted-foreground hover:text-foreground h-8 w-8 border border-border/55 bg-card shadow-[0_2px_8px_rgba(15,23,42,0.05)] hover:bg-card"
type="button"
/>
Name "Choose your AI":
<button
aria-label="Choose your AI"
class="thread-composer-model-badge group inline-flex w-fit min-w-0 max-w-[min(18rem,44vw)] appearance-none border-0 bg-transparent p-0 shadow-none cursor-pointer focus-visible:outline-none h-8"
data-state="closed"
type="button"
/>
Name "Voice input":
<button
aria-keyshortcuts="Control+Shift+D"
aria-label="Voice input"
class="inline-flex items-center justify-center whitespace-nowrap rounded-control text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 thread-composer-action touch-target rounded-full border border-transparent text-muted-foreground hover:bg-muted/65 hover:text-foreground h-8 w-8"
data-state="closed"
title="Click to dictate or hold"
type="button"
/>
Name "Configure model":
<button
aria-label="Configure model"
class="inline-flex items-center justify-center whitespace-nowrap rounded-control text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled
|
|
Python (Windows, 3.14)
Process completed with exit code 1.
|
|
Detect changes
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
|
|
webui
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
|
|
docker
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
|
|
Python (Windows process compatibility)
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4, actions/setup-python@v5, astral-sh/setup-uv@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
|
|
Python (Windows process compatibility)
Failed to save: <h2>Our services aren't available right now</h2><p>We're working to restore all services as soon as possible. Please check back soon.</p>0LNCgagAAAAAbYMZlpKOkTZ2HZxigqQxqU0xDMzFFREdFMDIyMQBFZGdl
|
|
Python (Windows process compatibility)
Failed to restore: Cache service responded with 400
|
|
Python (minimum, 3.11)
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4, actions/setup-python@v5, astral-sh/setup-uv@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
|
|
Python (minimum, 3.11)
Failed to save: <h2>Our services aren't available right now</h2><p>We're working to restore all services as soon as possible. Please check back soon.</p>0V9CgagAAAACBtpGD9GbOSIoFoU40LrQXU0xDMzFFREdFMDIxNwBFZGdl
|
|
Python (minimum, 3.11)
Failed to restore: Cache service responded with 400
|
|
Python (latest, 3.14 + coverage)
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4, actions/setup-python@v5, astral-sh/setup-uv@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
|
|
Python (latest, 3.14 + coverage)
Failed to save: <h2>Our services aren't available right now</h2><p>We're working to restore all services as soon as possible. Please check back soon.</p>0iNCgagAAAAAAujMn+JCSSbFWF/Zi9m3eQ08xRURHRTE5MjEARWRnZQ==
|
|
Python (latest, 3.14 + coverage)
Failed to restore: Cache service responded with 400
|
|
Python (Windows, 3.14)
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4, actions/setup-python@v5, astral-sh/setup-uv@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
|
|
Python (Windows, 3.14)
Failed to restore: Cache service responded with 400
|