result.status === "fail" ||
result.checks.some((check) => check.code === ADAPTER_AUTH_MISSING_CHECK_CODE);
@@ -844,6 +844,7 @@ function Setup({
{showModel && !usingKimiApi && (
0 && (
-
+
)}
diff --git a/ui/src/components/ui/select.tsx b/ui/src/components/ui/select.tsx
index 7f8e4ab4549..9c35a9b2409 100644
--- a/ui/src/components/ui/select.tsx
+++ b/ui/src/components/ui/select.tsx
@@ -4,6 +4,12 @@ import { Select as SelectPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
+export const nativeSelectClassName = "w-full rounded-md border border-border bg-background px-2.5 py-1.5 text-sm leading-5 outline-none focus-visible:ring-2 focus-visible:ring-ring"
+
+export function NativeSelect({ className, ...props }: React.ComponentProps<"select">) {
+ return
+}
+
// Shared with searchable picker triggers so their size and states match Select.
export const selectTriggerClassName = "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-base md:text-sm whitespace-nowrap shadow-xs transition-(--tp-color-box-shadow) outline-none focus-visible:ring-(length:--rad-3) disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
diff --git a/ui/storybook/stories/provider-routing-06-production.stories.tsx b/ui/storybook/stories/provider-routing-06-production.stories.tsx
index f93028f0b75..275622ea106 100644
--- a/ui/storybook/stories/provider-routing-06-production.stories.tsx
+++ b/ui/storybook/stories/provider-routing-06-production.stories.tsx
@@ -7,6 +7,8 @@ import { AiConnectionField } from "@/components/ai-connections/AiConnectionField
import { AgentProviderConnection, type ProviderConnection } from "@/components/new-agent/AgentProviderConnection";
import { useConnectionModels } from "@/components/ai-connections/useConnectionModels";
import { ModelDropdown } from "@/components/AgentConfigForm";
+import { Field } from "@/components/agent-config-primitives";
+import { NativeSelect } from "@/components/ui/select";
import { queryKeys } from "@/lib/queryKeys";
import type {
AiConnectionBinding,
@@ -107,13 +109,19 @@ function Connection() {
function OpenRouterModelPicker() {
const [open, setOpen] = useState(false);
const [value, setValue] = useState("");
+ const [effort, setEffort] = useState("");
const connection = useConnectionModels(companyId, {
provider: "openrouter", method: "api_key", mode: "shared",
connectionId: accounts[1].id, grantId: accounts[1].grantId,
}, "opencode_local");
- return
+ ;
+ preserveOrder creatable loadingModels={connection?.isLoading} />
+ setEffort(event.target.value)}>
+
+
+ ;
}
function OpenRouterModels() {
const [client] = useState(() => {
@@ -192,13 +200,18 @@ export const OpenRouterPopularModels: Story = {
render: () => ,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
- await userEvent.click(canvas.getByRole("button", { name: "Select model (required)" }));
- const body = within(canvasElement.ownerDocument.body);
- const labels = Array.from(canvasElement.ownerDocument.querySelectorAll('span[title^="openrouter/"]')).map(element => element.textContent);
+ const model = canvas.getByRole("combobox", { name: "Model" });
+ const labels = within(model).getAllByRole("option").filter(option => (option as HTMLOptionElement).value.startsWith("openrouter/")).map(option => option.textContent);
await expect(labels).toEqual(["Z.AI GLM 5", "Claude Sonnet 4.5", "OpenAI GPT-5.4"]);
- await userEvent.type(body.getByPlaceholderText("Search models... (type to create)"), "claude");
- await userEvent.click(body.getByRole("button", { name: "Claude Sonnet 4.5" }));
- await expect(canvas.getByRole("button", { name: "Claude Sonnet 4.5" })).toBeVisible();
+ await userEvent.selectOptions(model, "openrouter/anthropic/claude-sonnet-4.5");
+ await expect(model).toHaveValue("openrouter/anthropic/claude-sonnet-4.5");
+ await userEvent.selectOptions(canvas.getByRole("combobox", { name: "Thinking effort" }), "high");
+ await userEvent.selectOptions(model, within(model).getByRole("option", { name: "Enter custom model…" }));
+ await userEvent.clear(canvas.getByRole("textbox", { name: "Model ID" }));
+ await userEvent.type(canvas.getByRole("textbox", { name: "Model ID" }), "my-gateway/model-alias");
+ await expect(canvas.getByRole("textbox", { name: "Model ID" })).toHaveValue("my-gateway/model-alias");
+ await userEvent.selectOptions(model, "openrouter/anthropic/claude-sonnet-4.5");
+ await expect(canvas.queryByRole("textbox", { name: "Model ID" })).not.toBeInTheDocument();
},
};
export const ChooseProvider: Story = {