Switch reasoning, mode, plan, and fast from the Command Center (#2274)
* Switch reasoning, mode, plan, and fast from the Command Center Extend the Command Center (Cmd-K) with selectors for a focused agent's Thinking (reasoning effort), access Mode, Plan mode, and Fast mode, mirroring the model switcher. Typing surfaces tiered "<Setting> › <Value>" breadcrumb rows with icons and a check on the current value, for both a running agent and a new draft tab. Rows only appear while typing, so the default palette is unchanged. - Thinking and Mode are multi-option selects; Plan mode and Fast mode are On/Off toggles. - Plan mode is dual-natured: a permission mode for Claude/Copilot/OpenCode (excluded from the Mode list, given its own toggle) and the plan_mode feature for Codex. Fast mode drives the fast_mode feature (model-gated). - All logic lives in command-center/agent-setting-contributions.ts as pure, unit-tested builders, with a small icon helper and a hook that registers the running-agent selectors; the draft registers from its composer tab. No changes to the command palette component and no protocol changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Unify Command Center agent controls * refactor: clarify agent control ownership * refactor(app): tighten command center agent controls --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
Mohamed Boudra
parent
1ddca07d57
commit
35f985baff
@@ -1,16 +1,10 @@
|
||||
import { forwardRef, useCallback, type ComponentType } from "react";
|
||||
import { forwardRef, useCallback } from "react";
|
||||
import { Text, View, type PressableStateCallbackType } from "react-native";
|
||||
import { StyleSheet } from "react-native-unistyles";
|
||||
import { ComboboxTrigger } from "@/components/ui/combobox-trigger";
|
||||
import { useComposerControlLayout } from "@/composer/agent-controls/layout-context";
|
||||
import { ComposerToolbarGlyph } from "@/composer/agent-controls/glyph";
|
||||
|
||||
export interface AgentControlIconProps {
|
||||
size?: number;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export type AgentControlIcon = ComponentType<AgentControlIconProps>;
|
||||
import type { AgentControlIcon } from "@/agent-controls/icons";
|
||||
|
||||
interface AgentControlTriggerProps {
|
||||
icon: AgentControlIcon;
|
||||
|
||||
@@ -22,7 +22,9 @@ import {
|
||||
} from "react-native";
|
||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||
import { useShallow } from "zustand/shallow";
|
||||
import { Brain, ListTodo, Settings2, ShieldCheck, Zap } from "lucide-react-native";
|
||||
import { Settings2 } from "lucide-react-native";
|
||||
import { getAgentFeatureIcon, ThinkingIcon } from "@/agent-controls/icons";
|
||||
import { formatThinkingOptionLabel } from "@/agent-controls/labels";
|
||||
import { ComboboxTrigger } from "@/components/ui/combobox-trigger";
|
||||
import { CombinedModelSelector } from "@/components/combined-model-selector";
|
||||
import {
|
||||
@@ -58,16 +60,13 @@ import {
|
||||
getFeatureHighlightColor,
|
||||
getFeatureTooltip,
|
||||
getAgentControlHintKey,
|
||||
formatThinkingOptionLabel,
|
||||
resolveAgentModelSelection,
|
||||
} from "@/composer/agent-controls/utils";
|
||||
import { useIsCompactFormFactor } from "@/constants/layout";
|
||||
import { useToast } from "@/contexts/toast-context";
|
||||
import { toErrorMessage } from "@/utils/error-messages";
|
||||
import { showProviderNoticeToast } from "@/utils/provider-notice-toast";
|
||||
import { useCommandCenterActions } from "@/command-center/provider";
|
||||
import { buildModelChoiceContributions } from "@/command-center/model-contributions";
|
||||
import { getCommandCenterProviderIcon } from "@/command-center/provider-icon";
|
||||
import { useAgentControlCommandCenterActions } from "@/command-center/agent-control-registration";
|
||||
import { isNative } from "@/constants/platform";
|
||||
import {
|
||||
resolveComposerControlDensity,
|
||||
@@ -88,6 +87,8 @@ interface AgentControlOption {
|
||||
|
||||
type AgentControlSelector = "provider" | "mode" | "model" | "thinking" | `feature-${string}`;
|
||||
|
||||
const EMPTY_AGENT_PROVIDER_DEFINITIONS: AgentProviderDefinition[] = [];
|
||||
|
||||
interface ControlledAgentControlsProps {
|
||||
provider: string;
|
||||
providerOptions?: AgentControlOption[];
|
||||
@@ -164,14 +165,17 @@ function findOptionLabel(
|
||||
return selected?.label ?? fallback;
|
||||
}
|
||||
|
||||
const FEATURE_ICONS: Record<string, typeof Zap> = {
|
||||
"list-todo": ListTodo,
|
||||
"shield-check": ShieldCheck,
|
||||
zap: Zap,
|
||||
};
|
||||
function toCommandCenterModes(modeControl: AgentModeControlValue | null) {
|
||||
if (!modeControl) return undefined;
|
||||
return {
|
||||
options: modeControl.modeOptions,
|
||||
selectedId: modeControl.selectedModeId,
|
||||
select: modeControl.onSelectMode,
|
||||
};
|
||||
}
|
||||
|
||||
function getFeatureIcon(icon?: string) {
|
||||
return (icon && FEATURE_ICONS[icon]) || Settings2;
|
||||
function getModeProviderDefinitions(modeControl: AgentModeControlValue | null) {
|
||||
return modeControl?.providerDefinitions ?? EMPTY_AGENT_PROVIDER_DEFINITIONS;
|
||||
}
|
||||
|
||||
function getFeatureIconColor(
|
||||
@@ -923,7 +927,7 @@ function DesktopAgentControlsContent(props: DesktopAgentControlsContentProps) {
|
||||
<TooltipTrigger asChild triggerRefProp="ref">
|
||||
<AgentControlTrigger
|
||||
ref={thinkingAnchorRef}
|
||||
icon={Brain}
|
||||
icon={ThinkingIcon}
|
||||
surface="toolbar"
|
||||
label={t("agentControls.thinking.title")}
|
||||
value={displayThinking}
|
||||
@@ -1103,7 +1107,7 @@ function SheetAgentControlsContent(props: SheetAgentControlsContentProps) {
|
||||
<>
|
||||
<AgentControlTrigger
|
||||
ref={thinkingAnchorRef}
|
||||
icon={Brain}
|
||||
icon={ThinkingIcon}
|
||||
surface="sheet"
|
||||
label={t("agentControls.thinking.title")}
|
||||
value={displayThinking}
|
||||
@@ -1217,7 +1221,7 @@ function DesktopFeatureItem({
|
||||
);
|
||||
|
||||
if (feature.type === "toggle") {
|
||||
const FeatureIcon = getFeatureIcon(feature.icon);
|
||||
const FeatureIcon = getAgentFeatureIcon(feature.icon);
|
||||
return (
|
||||
<Tooltip delayDuration={0} enabledOnDesktop enabledOnMobile={false}>
|
||||
<TooltipTrigger asChild triggerRefProp="ref">
|
||||
@@ -1246,7 +1250,7 @@ function DesktopFeatureItem({
|
||||
}
|
||||
|
||||
if (feature.type === "select") {
|
||||
const FeatureIcon = getFeatureIcon(feature.icon);
|
||||
const FeatureIcon = getAgentFeatureIcon(feature.icon);
|
||||
const selectedOption = feature.options.find((o) => o.id === feature.value);
|
||||
return (
|
||||
<>
|
||||
@@ -1334,7 +1338,7 @@ function SheetFeatureItem({
|
||||
);
|
||||
|
||||
if (feature.type === "toggle") {
|
||||
const FeatureIcon = getFeatureIcon(feature.icon);
|
||||
const FeatureIcon = getAgentFeatureIcon(feature.icon);
|
||||
return (
|
||||
<AgentControlTrigger
|
||||
icon={FeatureIcon}
|
||||
@@ -1356,7 +1360,7 @@ function SheetFeatureItem({
|
||||
}
|
||||
|
||||
if (feature.type === "select") {
|
||||
const FeatureIcon = getFeatureIcon(feature.icon);
|
||||
const FeatureIcon = getAgentFeatureIcon(feature.icon);
|
||||
const selectedOption = feature.options.find((o) => o.id === feature.value);
|
||||
return (
|
||||
<>
|
||||
@@ -1402,7 +1406,7 @@ function ThinkingComboboxOption({
|
||||
onPress: () => void;
|
||||
iconColor: string;
|
||||
}) {
|
||||
const leadingSlot = useMemo(() => <Brain size={16} color={iconColor} />, [iconColor]);
|
||||
const leadingSlot = useMemo(() => <ThinkingIcon size={16} color={iconColor} />, [iconColor]);
|
||||
return (
|
||||
<ComboboxItem
|
||||
label={option.label}
|
||||
@@ -1421,7 +1425,6 @@ export const AgentControls = memo(function AgentControls({
|
||||
onDropdownClose,
|
||||
isCompactLayout,
|
||||
}: AgentControlsProps) {
|
||||
const { t } = useTranslation();
|
||||
const { preferences, updatePreferences } = useFormPreferences();
|
||||
const agent = useSessionStore(
|
||||
useShallow((state) => selectAgentControlsSlice(state, serverId, agentId)),
|
||||
@@ -1429,6 +1432,8 @@ export const AgentControls = memo(function AgentControls({
|
||||
const client = useSessionStore((state) => state.sessions[serverId]?.client ?? null);
|
||||
const toast = useToast();
|
||||
const modeControl = useLiveAgentModeControl(serverId, agentId);
|
||||
const commandCenterModes = toCommandCenterModes(modeControl);
|
||||
const modeProviderDefinitions = getModeProviderDefinitions(modeControl);
|
||||
|
||||
const {
|
||||
entries: snapshotEntries,
|
||||
@@ -1504,9 +1509,7 @@ export const AgentControls = memo(function AgentControls({
|
||||
mergeProviderPreferences({
|
||||
preferences: current,
|
||||
provider: agentProvider,
|
||||
updates: {
|
||||
model: modelId,
|
||||
},
|
||||
updates: { model: modelId },
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -1516,26 +1519,10 @@ export const AgentControls = memo(function AgentControls({
|
||||
},
|
||||
[agentId, agentProvider, client, toast, updatePreferences],
|
||||
);
|
||||
|
||||
const commandCenterModelActions = useMemo(
|
||||
() =>
|
||||
buildModelChoiceContributions({
|
||||
serverId,
|
||||
providers: agentModelSelectorProviders,
|
||||
selectedProvider: agentProvider ?? null,
|
||||
selectedModelId: activeModelId,
|
||||
groupLabel: t("shell.commandCenter.modelGroupLabel"),
|
||||
searchKeywords: t("shell.commandCenter.modelSearchKeywords"),
|
||||
getIcon: getCommandCenterProviderIcon,
|
||||
select: (_provider, modelId) => handleSelectModel(modelId),
|
||||
}),
|
||||
[activeModelId, agentModelSelectorProviders, agentProvider, handleSelectModel, serverId, t],
|
||||
const handleSelectCommandCenterModel = useCallback(
|
||||
(_provider: AgentProvider, modelId: string) => handleSelectModel(modelId),
|
||||
[handleSelectModel],
|
||||
);
|
||||
useCommandCenterActions({
|
||||
sourceId: `agent:${serverId}:${agentId}`,
|
||||
enabled: isPaneFocused && Boolean(client),
|
||||
actions: commandCenterModelActions,
|
||||
});
|
||||
|
||||
const handleToggleFavoriteModel = useCallback(
|
||||
(provider: string, modelId: string) => {
|
||||
@@ -1606,6 +1593,33 @@ export const AgentControls = memo(function AgentControls({
|
||||
[agentId, agentProvider, client, toast, updatePreferences],
|
||||
);
|
||||
|
||||
useAgentControlCommandCenterActions({
|
||||
sourceId: `agent:${serverId}:${agentId}`,
|
||||
enabled: isPaneFocused && Boolean(client),
|
||||
controls: {
|
||||
serverId,
|
||||
ownerKey: agentId,
|
||||
provider: agentProvider,
|
||||
providerDefinitions: modeProviderDefinitions,
|
||||
models: {
|
||||
providers: agentModelSelectorProviders,
|
||||
selectedProvider: agentProvider,
|
||||
selectedModelId: activeModelId,
|
||||
select: handleSelectCommandCenterModel,
|
||||
},
|
||||
thinking: {
|
||||
options: modelSelection.thinkingOptions,
|
||||
selectedId: modelSelection.selectedThinkingId,
|
||||
select: handleSelectThinkingOption,
|
||||
},
|
||||
modes: commandCenterModes,
|
||||
features: {
|
||||
list: agent?.features,
|
||||
set: handleSetFeature,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const handleModelSelectorOpen = useCallback(() => {
|
||||
refetchSnapshotIfStale(agentProvider);
|
||||
}, [agentProvider, refetchSnapshotIfStale]);
|
||||
|
||||
@@ -1,38 +1,15 @@
|
||||
import {
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
type ComponentType,
|
||||
type ReactElement,
|
||||
} from "react";
|
||||
import { useCallback, useMemo, useRef, useState, type ReactElement } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Text, View } from "react-native";
|
||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||
import { useShallow } from "zustand/shallow";
|
||||
import { useStoreWithEqualityFn } from "zustand/traditional";
|
||||
import {
|
||||
Bot,
|
||||
Shield,
|
||||
ShieldAlert,
|
||||
ShieldCheck,
|
||||
ShieldEllipsis,
|
||||
ShieldOff,
|
||||
ShieldPlus,
|
||||
ShieldQuestionMark,
|
||||
} from "lucide-react-native";
|
||||
import { type SheetHeader } from "@/components/adaptive-modal-sheet";
|
||||
import { Combobox, ComboboxItem, type ComboboxOption } from "@/components/ui/combobox";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { Shortcut } from "@/components/ui/shortcut";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useProvidersSnapshot } from "@/hooks/use-providers-snapshot";
|
||||
import { mergeProviderPreferences, useFormPreferences } from "@/hooks/use-form-preferences";
|
||||
import { resolveProviderDefinition } from "@/utils/provider-definitions";
|
||||
import { useToast } from "@/contexts/toast-context";
|
||||
import { toErrorMessage } from "@/utils/error-messages";
|
||||
import { showProviderNoticeToast } from "@/utils/provider-notice-toast";
|
||||
import { formatAgentModeLabel, getAgentControlHintKey } from "@/composer/agent-controls/utils";
|
||||
import { formatAgentModeLabel } from "@/agent-controls/labels";
|
||||
import { getAgentControlHintKey } from "@/composer/agent-controls/utils";
|
||||
import { useShortcutKeys } from "@/hooks/use-shortcut-keys";
|
||||
import { useKeyboardActionHandler } from "@/hooks/use-keyboard-action-handler";
|
||||
import type { KeyboardActionDefinition } from "@/keyboard/keyboard-action-dispatcher";
|
||||
@@ -40,25 +17,16 @@ import { resolveNextAgentModeId } from "@/composer/agent-controls/mode";
|
||||
import { useComposerKeyboardScope } from "@/composer/keyboard-scope";
|
||||
import { useComposerControlLayout } from "@/composer/agent-controls/layout-context";
|
||||
import { AgentControlTrigger } from "@/composer/agent-controls/control";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useProvidersSnapshot } from "@/hooks/use-providers-snapshot";
|
||||
import { mergeProviderPreferences, useFormPreferences } from "@/hooks/use-form-preferences";
|
||||
import { resolveProviderDefinition } from "@/utils/provider-definitions";
|
||||
import { useToast } from "@/contexts/toast-context";
|
||||
import { toErrorMessage } from "@/utils/error-messages";
|
||||
import { showProviderNoticeToast } from "@/utils/provider-notice-toast";
|
||||
import type { AgentMode } from "@getpaseo/protocol/agent-types";
|
||||
import { getModeVisuals, type AgentProviderDefinition } from "@getpaseo/protocol/provider-manifest";
|
||||
|
||||
interface ModeIconProps {
|
||||
size?: number;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
const MODE_ICONS: Record<string, ComponentType<ModeIconProps>> = {
|
||||
Bot,
|
||||
Shield,
|
||||
ShieldCheck,
|
||||
ShieldAlert,
|
||||
ShieldEllipsis,
|
||||
ShieldOff,
|
||||
ShieldPlus,
|
||||
ShieldQuestionMark,
|
||||
};
|
||||
|
||||
import type { AgentProviderDefinition } from "@getpaseo/protocol/provider-manifest";
|
||||
import { getAgentModeIcon, getAgentModeOptionIcon } from "@/agent-controls/icons";
|
||||
interface ModeComboboxOptionProps {
|
||||
option: ComboboxOption;
|
||||
selected: boolean;
|
||||
@@ -78,8 +46,7 @@ function ModeComboboxOption({
|
||||
providerDefinitions,
|
||||
iconColor,
|
||||
}: ModeComboboxOptionProps) {
|
||||
const visuals = getModeVisuals(provider, option.id, providerDefinitions);
|
||||
const IconComponent = visuals?.icon ? MODE_ICONS[visuals.icon] : undefined;
|
||||
const IconComponent = getAgentModeOptionIcon(provider, option.id, providerDefinitions);
|
||||
const leadingSlot = useMemo(
|
||||
() => (IconComponent ? <IconComponent size={16} color={iconColor} /> : null),
|
||||
[IconComponent, iconColor],
|
||||
@@ -134,10 +101,7 @@ export function AgentModeControl({
|
||||
return modeOptions.find((m) => m.id === selectedModeId) ?? modeOptions[0];
|
||||
}, [modeOptions, selectedModeId]);
|
||||
|
||||
const visuals = selectedMode
|
||||
? getModeVisuals(provider, selectedMode.id, providerDefinitions)
|
||||
: undefined;
|
||||
const Icon = visuals?.icon ? (MODE_ICONS[visuals.icon] ?? Bot) : Bot;
|
||||
const Icon = getAgentModeIcon(provider, selectedMode?.id ?? "", providerDefinitions);
|
||||
const iconColor = theme.colors.foregroundMuted;
|
||||
const selectedModeLabel = selectedMode ? formatAgentModeLabel(selectedMode) : "";
|
||||
|
||||
@@ -316,9 +280,7 @@ export function useLiveAgentModeControl(
|
||||
mergeProviderPreferences({
|
||||
preferences: current,
|
||||
provider: slice.provider,
|
||||
updates: {
|
||||
mode: modeId || undefined,
|
||||
},
|
||||
updates: { mode: modeId || undefined },
|
||||
}),
|
||||
).catch((error) => {
|
||||
console.warn("[AgentModeControl] persist mode preference failed", error);
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
formatAgentModeLabel,
|
||||
getFeatureHighlightColor,
|
||||
getFeatureTooltip,
|
||||
getAgentControlHintKey,
|
||||
formatThinkingOptionLabel,
|
||||
normalizeModelId,
|
||||
resolveAgentModelSelection,
|
||||
} from "./utils";
|
||||
@@ -54,36 +52,6 @@ describe("normalizeModelId", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatAgentModeLabel", () => {
|
||||
it("sentence-cases provider mode labels", () => {
|
||||
expect(formatAgentModeLabel({ id: "plan", label: "Plan" })).toBe("Plan");
|
||||
expect(formatAgentModeLabel({ id: "full-access", label: "Full Access" })).toBe("Full access");
|
||||
expect(formatAgentModeLabel({ id: "auto-review", label: "Auto-review" })).toBe("Auto-review");
|
||||
expect(formatAgentModeLabel({ id: "read_only", label: "read_only" })).toBe("Read only");
|
||||
expect(formatAgentModeLabel({ id: "acceptEdits", label: "acceptEdits" })).toBe("Accept edits");
|
||||
});
|
||||
|
||||
it("splits compact mode ids when no provider label is available", () => {
|
||||
expect(formatAgentModeLabel({ id: "auto-review" })).toBe("Auto review");
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatThinkingOptionLabel", () => {
|
||||
it("formats compact thinking option labels for display", () => {
|
||||
expect(formatThinkingOptionLabel({ id: "none", label: "none" })).toBe("None");
|
||||
expect(formatThinkingOptionLabel({ id: "low", label: "low" })).toBe("Low");
|
||||
expect(formatThinkingOptionLabel({ id: "medium", label: "medium" })).toBe("Medium");
|
||||
expect(formatThinkingOptionLabel({ id: "high", label: "high" })).toBe("High");
|
||||
expect(formatThinkingOptionLabel({ id: "xhigh", label: "xhigh" })).toBe("Extra high");
|
||||
});
|
||||
|
||||
it("sentence-cases split provider labels", () => {
|
||||
expect(formatThinkingOptionLabel({ id: "extra_high", label: "extra_high" })).toBe("Extra high");
|
||||
expect(formatThinkingOptionLabel({ id: "think-hard", label: "think-hard" })).toBe("Think hard");
|
||||
expect(formatThinkingOptionLabel({ id: "xhigh", label: "XHigh" })).toBe("Extra high");
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveAgentModelSelection", () => {
|
||||
it("prefers runtime model over configured model", () => {
|
||||
const selection = resolveAgentModelSelection({
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { AgentFeature, AgentModelDefinition } from "@getpaseo/protocol/agent-types";
|
||||
import { i18n } from "@/i18n/i18next";
|
||||
import { formatThinkingOptionLabel } from "@/agent-controls/labels";
|
||||
import { FAST_MODE_FEATURE_ID, PLAN_MODE_FEATURE_ID } from "@/agent-controls/policy";
|
||||
|
||||
export type ExplainedAgentControl = "mode" | "model" | "thinking";
|
||||
export type FeatureHighlightColor = "blue" | "default" | "green" | "yellow";
|
||||
@@ -35,61 +37,17 @@ export function getFeatureTooltip(feature: Pick<AgentFeature, "label" | "tooltip
|
||||
|
||||
export function getFeatureHighlightColor(featureId: string): FeatureHighlightColor {
|
||||
switch (featureId) {
|
||||
case "fast_mode":
|
||||
case FAST_MODE_FEATURE_ID:
|
||||
return "yellow";
|
||||
case "auto_accept":
|
||||
return "green";
|
||||
case "plan_mode":
|
||||
case PLAN_MODE_FEATURE_ID:
|
||||
return "blue";
|
||||
default:
|
||||
return "default";
|
||||
}
|
||||
}
|
||||
|
||||
interface ControlLabelInput {
|
||||
id: string;
|
||||
label?: string | null;
|
||||
}
|
||||
|
||||
function sentenceCase(value: string): string {
|
||||
if (!value) {
|
||||
return value;
|
||||
}
|
||||
return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
|
||||
}
|
||||
|
||||
function splitCompactLabel(value: string, splitHyphen: boolean): string {
|
||||
const separatorPattern = splitHyphen ? /[_-]+/g : /_+/g;
|
||||
|
||||
return value
|
||||
.replace(separatorPattern, " ")
|
||||
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
||||
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function formatControlLabel(option: ControlLabelInput, splitHyphen: boolean): string {
|
||||
const rawLabel = (option.label ?? option.id).trim();
|
||||
return sentenceCase(splitCompactLabel(rawLabel, splitHyphen));
|
||||
}
|
||||
|
||||
export function formatAgentModeLabel(mode: ControlLabelInput): string {
|
||||
return formatControlLabel(mode, mode.label == null);
|
||||
}
|
||||
|
||||
export function formatThinkingOptionLabel(option: ControlLabelInput): string {
|
||||
const rawLabel = (option.label ?? option.id).trim();
|
||||
const compactId = option.id.replace(/[\s_-]+/g, "").toLowerCase();
|
||||
const compactLabel = rawLabel.replace(/[\s_-]+/g, "").toLowerCase();
|
||||
|
||||
if (compactId === "xhigh" || compactLabel === "xhigh") {
|
||||
return i18n.t("agentControls.thinking.extraHigh");
|
||||
}
|
||||
|
||||
return formatControlLabel(option, true);
|
||||
}
|
||||
|
||||
function findModelById(
|
||||
models: AgentModelDefinition[] | null,
|
||||
modelId: string | null,
|
||||
|
||||
@@ -23,9 +23,7 @@ import { useCreateFlowStore } from "@/stores/create-flow-store";
|
||||
import type { Agent } from "@/stores/session-store";
|
||||
import { useWorkspaceFields } from "@/stores/session-store-hooks";
|
||||
import { useWorkspaceDraftSubmissionStore } from "@/stores/workspace-draft-submission-store";
|
||||
import { useCommandCenterActions } from "@/command-center/provider";
|
||||
import { buildModelChoiceContributions } from "@/command-center/model-contributions";
|
||||
import { getCommandCenterProviderIcon } from "@/command-center/provider-icon";
|
||||
import { useAgentControlCommandCenterActions } from "@/command-center/agent-control-registration";
|
||||
import { encodeImages } from "@/utils/encode-images";
|
||||
import type { WorkspaceFileOpenRequest } from "@/workspace/file-open";
|
||||
import { shouldAutoFocusWorkspaceDraftComposer } from "@/screens/workspace/workspace-draft-pane-focus";
|
||||
@@ -378,27 +376,17 @@ export function WorkspaceDraftAgentTab({
|
||||
throw new Error("Workspace draft composer state is required");
|
||||
}
|
||||
|
||||
const draftModelActions = useMemo(
|
||||
() =>
|
||||
buildModelChoiceContributions({
|
||||
serverId,
|
||||
providers: composerState.modelSelectorProviders,
|
||||
selectedProvider: composerState.selectedProvider,
|
||||
selectedModelId: composerState.effectiveModelId || null,
|
||||
groupLabel: t("shell.commandCenter.modelGroupLabel"),
|
||||
searchKeywords: t("shell.commandCenter.modelSearchKeywords"),
|
||||
getIcon: getCommandCenterProviderIcon,
|
||||
select: composerState.setProviderAndModelFromUser,
|
||||
}),
|
||||
[
|
||||
composerState.effectiveModelId,
|
||||
composerState.modelSelectorProviders,
|
||||
composerState.selectedProvider,
|
||||
composerState.setProviderAndModelFromUser,
|
||||
serverId,
|
||||
t,
|
||||
],
|
||||
);
|
||||
const draftProvider = composerState.selectedProvider;
|
||||
const draftProviderDefinitions = composerState.providerDefinitions;
|
||||
const draftThinkingOptions = composerState.availableThinkingOptions;
|
||||
const draftSelectedThinkingId = composerState.selectedThinkingOptionId;
|
||||
const draftSetThinkingOption = composerState.setThinkingOptionFromUser;
|
||||
const draftModeOptions = composerState.modeOptions;
|
||||
const draftSelectedMode = composerState.selectedMode;
|
||||
const draftSetMode = composerState.setModeFromUser;
|
||||
const draftFeatures = composerState.agentControls.features;
|
||||
const draftOnSetFeature = composerState.agentControls.onSetFeature;
|
||||
|
||||
const clearDraftInput = draftInput.clear;
|
||||
const setDraftText = draftInput.setText;
|
||||
const setDraftAttachments = draftInput.setAttachments;
|
||||
@@ -541,12 +529,36 @@ export function WorkspaceDraftAgentTab({
|
||||
onCreated(result);
|
||||
},
|
||||
});
|
||||
useCommandCenterActions({
|
||||
useAgentControlCommandCenterActions({
|
||||
sourceId: `draft:${serverId}:${tabId}`,
|
||||
enabled: isPaneFocused && !isSubmitting,
|
||||
actions: draftModelActions,
|
||||
controls: {
|
||||
serverId,
|
||||
ownerKey: tabId,
|
||||
provider: draftProvider,
|
||||
providerDefinitions: draftProviderDefinitions,
|
||||
models: {
|
||||
providers: composerState.modelSelectorProviders,
|
||||
selectedProvider: draftProvider,
|
||||
selectedModelId: composerState.effectiveModelId,
|
||||
select: composerState.setProviderAndModelFromUser,
|
||||
},
|
||||
thinking: {
|
||||
options: draftThinkingOptions,
|
||||
selectedId: draftSelectedThinkingId,
|
||||
select: draftSetThinkingOption,
|
||||
},
|
||||
modes: {
|
||||
options: draftModeOptions,
|
||||
selectedId: draftSelectedMode,
|
||||
select: draftSetMode,
|
||||
},
|
||||
features: {
|
||||
list: draftFeatures,
|
||||
set: draftOnSetFeature,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const isReadyForPendingAutoSubmit = Boolean(
|
||||
pendingAutoSubmit &&
|
||||
draftInput.isHydrated &&
|
||||
|
||||
Reference in New Issue
Block a user