From 688ebe1c9fcfdb82ae1b38f13b10cba60c5df146 Mon Sep 17 00:00:00 2001 From: Jiang Bohan Date: Wed, 20 May 2026 13:33:58 +0800 Subject: [PATCH] feat(agent): inspector picker for thinking_level (MUL-2339) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR1 (#2865) shipped the backend — column, daemon-side discovery, Claude/Codex injection, API validation — but the agent detail inspector had no UI to set the value. Users could only configure thinking_level via custom_env / API. This wires up the picker so it lives next to Runtime and Model where everything else editable already lives. Picker is per-(runtime, model): it reuses the same `runtimeModelsOptions` query the Model picker already runs (60s cache, no extra round-trip) and reads the active model's `thinking.supported_levels`. When the list is empty — every provider except Claude/Codex today, or a Claude model that doesn't expose `--effort` — the entire PropRow is hidden, not just rendered inert. The picker never gets to invent value/label pairs itself; they come verbatim from each CLI's own catalog (`Low`, `Extra high`, …) so the user sees exactly what `claude --effort` / `/effort` and Codex's TUI show. The `default_level` from the catalog is badged inside the popover so the user knows which value `""` (the persisted "use model default" sentinel) maps to. The clear footer sends `""` explicitly, which the backend already understands as the tri-state "explicit clear" branch of UpdateAgent. Invalid combinations (e.g. picking a value not in the target provider's enum after a runtime swap in the same PATCH) hit the existing 400 path on the server and surface as a toast via the inspector's standard `onUpdate` error handler — no extra client-side guard needed. Exports `RuntimeModelThinking` and `RuntimeModelThinkingLevel` from `@multica/core/types` so views consumers can refer to them by name. i18n keys added in EN and zh-Hans (parity test green). Co-authored-by: multica-agent --- packages/core/types/index.ts | 2 + .../components/agent-detail-inspector.tsx | 65 +++++++++ .../components/inspector/thinking-picker.tsx | 127 ++++++++++++++++++ packages/views/locales/en/agents.json | 8 +- packages/views/locales/zh-Hans/agents.json | 8 +- 5 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 packages/views/agents/components/inspector/thinking-picker.tsx diff --git a/packages/core/types/index.ts b/packages/core/types/index.ts index d5dcd0045b..a5fa932c44 100644 --- a/packages/core/types/index.ts +++ b/packages/core/types/index.ts @@ -36,6 +36,8 @@ export type { RuntimeUpdate, RuntimeUpdateStatus, RuntimeModel, + RuntimeModelThinking, + RuntimeModelThinkingLevel, RuntimeModelListRequest, RuntimeModelListStatus, RuntimeModelsResult, diff --git a/packages/views/agents/components/agent-detail-inspector.tsx b/packages/views/agents/components/agent-detail-inspector.tsx index 103ad86d9b..f796960a63 100644 --- a/packages/views/agents/components/agent-detail-inspector.tsx +++ b/packages/views/agents/components/agent-detail-inspector.tsx @@ -8,10 +8,12 @@ import { } from "react"; import { Camera, Loader2, Pencil } from "lucide-react"; import { toast } from "sonner"; +import { useQuery } from "@tanstack/react-query"; import type { Agent, AgentRuntime, MemberWithUser, + RuntimeModel, } from "@multica/core/types"; import { AGENT_DESCRIPTION_MAX_LENGTH, @@ -19,6 +21,7 @@ import { } from "@multica/core/agents"; import { api } from "@multica/core/api"; import { useFileUpload } from "@multica/core/hooks/use-file-upload"; +import { runtimeModelsOptions } from "@multica/core/runtimes"; import { isImeComposing, timeAgo } from "@multica/core/utils"; import { Button } from "@multica/ui/components/ui/button"; import { ActorAvatar } from "../../common/actor-avatar"; @@ -43,6 +46,7 @@ import { ConcurrencyPicker } from "./inspector/concurrency-picker"; import { ModelPicker } from "./inspector/model-picker"; import { RuntimePicker } from "./inspector/runtime-picker"; import { SkillAttach } from "./inspector/skill-attach"; +import { ThinkingPicker } from "./inspector/thinking-picker"; import { VisibilityPicker } from "./inspector/visibility-picker"; interface InspectorProps { @@ -130,6 +134,14 @@ export function AgentDetailInspector({ onChange={(m) => update({ model: m })} /> + update({ thinking_level: v })} + /> $.inspector.prop_visibility)} interactive={false}>