feat: add default variant support in settings and UI components
This commit is contained in:
@@ -234,13 +234,27 @@ fn sanitize_settings_update(payload: &Value) -> Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(Value::String(s)) = obj.get("defaultModel") {
|
if let Some(Value::String(s)) = obj.get("defaultModel") {
|
||||||
if !s.is_empty() {
|
let trimmed = s.trim();
|
||||||
result_obj.insert("defaultModel".to_string(), json!(s));
|
if trimmed.is_empty() {
|
||||||
|
result_obj.insert("defaultModel".to_string(), Value::Null);
|
||||||
|
} else {
|
||||||
|
result_obj.insert("defaultModel".to_string(), json!(trimmed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(Value::String(s)) = obj.get("defaultVariant") {
|
||||||
|
let trimmed = s.trim();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
result_obj.insert("defaultVariant".to_string(), Value::Null);
|
||||||
|
} else {
|
||||||
|
result_obj.insert("defaultVariant".to_string(), json!(trimmed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(Value::String(s)) = obj.get("defaultAgent") {
|
if let Some(Value::String(s)) = obj.get("defaultAgent") {
|
||||||
if !s.is_empty() {
|
let trimmed = s.trim();
|
||||||
result_obj.insert("defaultAgent".to_string(), json!(s));
|
if trimmed.is_empty() {
|
||||||
|
result_obj.insert("defaultAgent".to_string(), Value::Null);
|
||||||
|
} else {
|
||||||
|
result_obj.insert("defaultAgent".to_string(), json!(trimmed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -276,6 +276,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
|||||||
currentModelId,
|
currentModelId,
|
||||||
currentVariant,
|
currentVariant,
|
||||||
currentAgentName,
|
currentAgentName,
|
||||||
|
settingsDefaultVariant,
|
||||||
setProvider,
|
setProvider,
|
||||||
setModel,
|
setModel,
|
||||||
setCurrentVariant,
|
setCurrentVariant,
|
||||||
@@ -865,7 +866,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
|||||||
}, [currentAgentName, currentSessionId, getAgentModelForSession, tryApplyModelSelection, agents, contextHydrated]);
|
}, [currentAgentName, currentSessionId, getAgentModelForSession, tryApplyModelSelection, agents, contextHydrated]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!contextHydrated || !currentSessionId || !currentAgentName) {
|
if (!contextHydrated || !currentAgentName) {
|
||||||
setCurrentVariant(undefined);
|
setCurrentVariant(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -875,6 +876,28 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (availableVariants.length === 0) {
|
||||||
|
setCurrentVariant(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentVariant && !availableVariants.includes(currentVariant)) {
|
||||||
|
setCurrentVariant(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draft state (no session yet): seed from settings default, but don't override
|
||||||
|
// user selection while drafting.
|
||||||
|
if (!currentSessionId) {
|
||||||
|
if (!currentVariant) {
|
||||||
|
const desired = settingsDefaultVariant && availableVariants.includes(settingsDefaultVariant)
|
||||||
|
? settingsDefaultVariant
|
||||||
|
: undefined;
|
||||||
|
setCurrentVariant(desired);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const savedVariant = getAgentModelVariantForSession(
|
const savedVariant = getAgentModelVariantForSession(
|
||||||
currentSessionId,
|
currentSessionId,
|
||||||
currentAgentName,
|
currentAgentName,
|
||||||
@@ -882,12 +905,11 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
|||||||
currentModelId,
|
currentModelId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (savedVariant && !availableVariants.includes(savedVariant)) {
|
const resolvedSaved = savedVariant && availableVariants.includes(savedVariant)
|
||||||
setCurrentVariant(undefined);
|
? savedVariant
|
||||||
return;
|
: undefined;
|
||||||
}
|
|
||||||
|
|
||||||
setCurrentVariant(savedVariant);
|
setCurrentVariant(resolvedSaved);
|
||||||
}, [
|
}, [
|
||||||
availableVariants,
|
availableVariants,
|
||||||
contextHydrated,
|
contextHydrated,
|
||||||
@@ -895,8 +917,10 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
|||||||
currentAgentName,
|
currentAgentName,
|
||||||
currentProviderId,
|
currentProviderId,
|
||||||
currentModelId,
|
currentModelId,
|
||||||
|
currentVariant,
|
||||||
getAgentModelVariantForSession,
|
getAgentModelVariantForSession,
|
||||||
setCurrentVariant,
|
setCurrentVariant,
|
||||||
|
settingsDefaultVariant,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleVariantSelect = React.useCallback((variant: string | undefined) => {
|
const handleVariantSelect = React.useCallback((variant: string | undefined) => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { RiInformationLine } from '@remixicon/react';
|
|||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { ModelSelector } from '@/components/sections/agents/ModelSelector';
|
import { ModelSelector } from '@/components/sections/agents/ModelSelector';
|
||||||
import { AgentSelector } from '@/components/sections/commands/AgentSelector';
|
import { AgentSelector } from '@/components/sections/commands/AgentSelector';
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { updateDesktopSettings } from '@/lib/persistence';
|
import { updateDesktopSettings } from '@/lib/persistence';
|
||||||
import { isDesktopRuntime, getDesktopSettings } from '@/lib/desktop';
|
import { isDesktopRuntime, getDesktopSettings } from '@/lib/desktop';
|
||||||
import { useConfigStore } from '@/stores/useConfigStore';
|
import { useConfigStore } from '@/stores/useConfigStore';
|
||||||
@@ -13,13 +14,16 @@ export const DefaultsSettings: React.FC = () => {
|
|||||||
const setProvider = useConfigStore((state) => state.setProvider);
|
const setProvider = useConfigStore((state) => state.setProvider);
|
||||||
const setModel = useConfigStore((state) => state.setModel);
|
const setModel = useConfigStore((state) => state.setModel);
|
||||||
const setAgent = useConfigStore((state) => state.setAgent);
|
const setAgent = useConfigStore((state) => state.setAgent);
|
||||||
|
const setCurrentVariant = useConfigStore((state) => state.setCurrentVariant);
|
||||||
const setSettingsDefaultModel = useConfigStore((state) => state.setSettingsDefaultModel);
|
const setSettingsDefaultModel = useConfigStore((state) => state.setSettingsDefaultModel);
|
||||||
|
const setSettingsDefaultVariant = useConfigStore((state) => state.setSettingsDefaultVariant);
|
||||||
const setSettingsDefaultAgent = useConfigStore((state) => state.setSettingsDefaultAgent);
|
const setSettingsDefaultAgent = useConfigStore((state) => state.setSettingsDefaultAgent);
|
||||||
const settingsAutoCreateWorktree = useConfigStore((state) => state.settingsAutoCreateWorktree);
|
const settingsAutoCreateWorktree = useConfigStore((state) => state.settingsAutoCreateWorktree);
|
||||||
const setSettingsAutoCreateWorktree = useConfigStore((state) => state.setSettingsAutoCreateWorktree);
|
const setSettingsAutoCreateWorktree = useConfigStore((state) => state.setSettingsAutoCreateWorktree);
|
||||||
const providers = useConfigStore((state) => state.providers);
|
const providers = useConfigStore((state) => state.providers);
|
||||||
|
|
||||||
const [defaultModel, setDefaultModel] = React.useState<string | undefined>();
|
const [defaultModel, setDefaultModel] = React.useState<string | undefined>();
|
||||||
|
const [defaultVariant, setDefaultVariant] = React.useState<string | undefined>();
|
||||||
const [defaultAgent, setDefaultAgent] = React.useState<string | undefined>();
|
const [defaultAgent, setDefaultAgent] = React.useState<string | undefined>();
|
||||||
const [isLoading, setIsLoading] = React.useState(true);
|
const [isLoading, setIsLoading] = React.useState(true);
|
||||||
|
|
||||||
@@ -35,7 +39,7 @@ export const DefaultsSettings: React.FC = () => {
|
|||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const loadSettings = async () => {
|
const loadSettings = async () => {
|
||||||
try {
|
try {
|
||||||
let data: { defaultModel?: string; defaultAgent?: string } | null = null;
|
let data: { defaultModel?: string; defaultVariant?: string; defaultAgent?: string } | null = null;
|
||||||
|
|
||||||
// 1. Desktop runtime (Tauri)
|
// 1. Desktop runtime (Tauri)
|
||||||
if (isDesktopRuntime()) {
|
if (isDesktopRuntime()) {
|
||||||
@@ -50,6 +54,7 @@ export const DefaultsSettings: React.FC = () => {
|
|||||||
if (settings) {
|
if (settings) {
|
||||||
data = {
|
data = {
|
||||||
defaultModel: typeof settings.defaultModel === 'string' ? settings.defaultModel : undefined,
|
defaultModel: typeof settings.defaultModel === 'string' ? settings.defaultModel : undefined,
|
||||||
|
defaultVariant: typeof (settings as Record<string, unknown>).defaultVariant === 'string' ? ((settings as Record<string, unknown>).defaultVariant as string) : undefined,
|
||||||
defaultAgent: typeof settings.defaultAgent === 'string' ? settings.defaultAgent : undefined,
|
defaultAgent: typeof settings.defaultAgent === 'string' ? settings.defaultAgent : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -71,8 +76,13 @@ export const DefaultsSettings: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
setDefaultModel(data.defaultModel);
|
const model = typeof data.defaultModel === 'string' && data.defaultModel.trim().length > 0 ? data.defaultModel.trim() : undefined;
|
||||||
setDefaultAgent(data.defaultAgent);
|
const variant = typeof data.defaultVariant === 'string' && data.defaultVariant.trim().length > 0 ? data.defaultVariant.trim() : undefined;
|
||||||
|
const agent = typeof data.defaultAgent === 'string' && data.defaultAgent.trim().length > 0 ? data.defaultAgent.trim() : undefined;
|
||||||
|
|
||||||
|
setDefaultModel(model);
|
||||||
|
setDefaultVariant(variant);
|
||||||
|
setDefaultAgent(agent);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Failed to load defaults settings:', error);
|
console.warn('Failed to load defaults settings:', error);
|
||||||
@@ -87,6 +97,11 @@ export const DefaultsSettings: React.FC = () => {
|
|||||||
const newValue = providerId && modelId ? `${providerId}/${modelId}` : undefined;
|
const newValue = providerId && modelId ? `${providerId}/${modelId}` : undefined;
|
||||||
setDefaultModel(newValue);
|
setDefaultModel(newValue);
|
||||||
|
|
||||||
|
// Reset variant when model changes (model-specific)
|
||||||
|
setDefaultVariant(undefined);
|
||||||
|
setSettingsDefaultVariant(undefined);
|
||||||
|
setCurrentVariant(undefined);
|
||||||
|
|
||||||
// Update config store settings default (used by setAgent logic)
|
// Update config store settings default (used by setAgent logic)
|
||||||
setSettingsDefaultModel(newValue);
|
setSettingsDefaultModel(newValue);
|
||||||
|
|
||||||
@@ -102,11 +117,29 @@ export const DefaultsSettings: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
await updateDesktopSettings({
|
await updateDesktopSettings({
|
||||||
defaultModel: newValue ?? '',
|
defaultModel: newValue ?? '',
|
||||||
|
defaultVariant: '',
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Failed to save default model:', error);
|
console.warn('Failed to save default model:', error);
|
||||||
}
|
}
|
||||||
}, [providers, setProvider, setModel, setSettingsDefaultModel]);
|
}, [providers, setCurrentVariant, setProvider, setModel, setSettingsDefaultModel, setSettingsDefaultVariant]);
|
||||||
|
|
||||||
|
const DEFAULT_VARIANT_VALUE = '__default__';
|
||||||
|
|
||||||
|
const handleVariantChange = React.useCallback(async (variant: string) => {
|
||||||
|
const newValue = variant === DEFAULT_VARIANT_VALUE ? undefined : (variant || undefined);
|
||||||
|
setDefaultVariant(newValue);
|
||||||
|
setSettingsDefaultVariant(newValue);
|
||||||
|
setCurrentVariant(newValue);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateDesktopSettings({
|
||||||
|
defaultVariant: newValue ?? '',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to save default variant:', error);
|
||||||
|
}
|
||||||
|
}, [setCurrentVariant, setSettingsDefaultVariant]);
|
||||||
|
|
||||||
const handleAgentChange = React.useCallback(async (agentName: string) => {
|
const handleAgentChange = React.useCallback(async (agentName: string) => {
|
||||||
const newValue = agentName || undefined;
|
const newValue = agentName || undefined;
|
||||||
@@ -129,6 +162,31 @@ export const DefaultsSettings: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [setAgent, setSettingsDefaultAgent]);
|
}, [setAgent, setSettingsDefaultAgent]);
|
||||||
|
|
||||||
|
const availableVariants = React.useMemo(() => {
|
||||||
|
const provider = providers.find((p) => p.id === parsedModel.providerId);
|
||||||
|
const model = provider?.models.find((m: Record<string, unknown>) => (m as { id?: string }).id === parsedModel.modelId) as
|
||||||
|
| { variants?: Record<string, unknown> }
|
||||||
|
| undefined;
|
||||||
|
const variants = model?.variants;
|
||||||
|
if (!variants) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return Object.keys(variants);
|
||||||
|
}, [parsedModel.modelId, parsedModel.providerId, providers]);
|
||||||
|
|
||||||
|
const supportsVariants = availableVariants.length > 0;
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!supportsVariants && defaultVariant) {
|
||||||
|
setDefaultVariant(undefined);
|
||||||
|
setSettingsDefaultVariant(undefined);
|
||||||
|
setCurrentVariant(undefined);
|
||||||
|
updateDesktopSettings({ defaultVariant: '' }).catch(() => {
|
||||||
|
// best effort
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [defaultVariant, setCurrentVariant, setSettingsDefaultVariant, supportsVariants]);
|
||||||
|
|
||||||
const handleAutoWorktreeChange = React.useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleAutoWorktreeChange = React.useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const enabled = e.target.checked;
|
const enabled = e.target.checked;
|
||||||
setSettingsAutoCreateWorktree(enabled);
|
setSettingsAutoCreateWorktree(enabled);
|
||||||
@@ -161,29 +219,53 @@ export const DefaultsSettings: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
<label className="typography-ui-label text-muted-foreground">Default model</label>
|
<label className="typography-ui-label text-muted-foreground">Default model</label>
|
||||||
<ModelSelector
|
<ModelSelector
|
||||||
providerId={parsedModel.providerId}
|
providerId={parsedModel.providerId}
|
||||||
modelId={parsedModel.modelId}
|
modelId={parsedModel.modelId}
|
||||||
onChange={handleModelChange}
|
onChange={handleModelChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-1.5">
|
{supportsVariants && (
|
||||||
<label className="typography-ui-label text-muted-foreground">Default agent</label>
|
<div className="flex flex-col gap-1.5">
|
||||||
<AgentSelector
|
<label className="typography-ui-label text-muted-foreground">Default thinking</label>
|
||||||
agentName={defaultAgent || ''}
|
<Select value={defaultVariant ?? DEFAULT_VARIANT_VALUE} onValueChange={handleVariantChange}>
|
||||||
onChange={handleAgentChange}
|
<SelectTrigger className="w-auto max-w-xs typography-meta text-foreground">
|
||||||
/>
|
<SelectValue placeholder="Thinking" />
|
||||||
</div>
|
</SelectTrigger>
|
||||||
</div>
|
<SelectContent>
|
||||||
|
<SelectItem value={DEFAULT_VARIANT_VALUE} className="pr-2 [&>span:first-child]:hidden">Default</SelectItem>
|
||||||
|
{availableVariants.map((variant) => (
|
||||||
|
<SelectItem key={variant} value={variant} className="pr-2 [&>span:first-child]:hidden">
|
||||||
|
{variant}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<label className="typography-ui-label text-muted-foreground">Default agent</label>
|
||||||
|
<AgentSelector
|
||||||
|
agentName={defaultAgent || ''}
|
||||||
|
onChange={handleAgentChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{(defaultModel || defaultAgent) && (
|
{(defaultModel || defaultAgent) && (
|
||||||
<div className="typography-meta text-muted-foreground">
|
<div className="typography-meta text-muted-foreground">
|
||||||
New sessions will start with:{' '}
|
New sessions will start with:{' '}
|
||||||
{defaultModel && <span className="text-foreground">{defaultModel}</span>}
|
{defaultModel && (
|
||||||
|
<span className="text-foreground">
|
||||||
|
{defaultModel}
|
||||||
|
{supportsVariants ? ` (${defaultVariant ?? 'default'})` : ''}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{defaultModel && defaultAgent && ' / '}
|
{defaultModel && defaultAgent && ' / '}
|
||||||
{defaultAgent && <span className="text-foreground">{defaultAgent}</span>}
|
{defaultAgent && <span className="text-foreground">{defaultAgent}</span>}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export type DesktopSettings = {
|
|||||||
autoDeleteEnabled?: boolean;
|
autoDeleteEnabled?: boolean;
|
||||||
autoDeleteAfterDays?: number;
|
autoDeleteAfterDays?: number;
|
||||||
defaultModel?: string; // format: "provider/model"
|
defaultModel?: string; // format: "provider/model"
|
||||||
|
defaultVariant?: string;
|
||||||
defaultAgent?: string;
|
defaultAgent?: string;
|
||||||
autoCreateWorktree?: boolean;
|
autoCreateWorktree?: boolean;
|
||||||
queueModeEnabled?: boolean;
|
queueModeEnabled?: boolean;
|
||||||
|
|||||||
@@ -252,6 +252,9 @@ const sanitizeWebSettings = (payload: unknown): DesktopSettings | null => {
|
|||||||
if (typeof candidate.defaultModel === 'string' && candidate.defaultModel.length > 0) {
|
if (typeof candidate.defaultModel === 'string' && candidate.defaultModel.length > 0) {
|
||||||
result.defaultModel = candidate.defaultModel;
|
result.defaultModel = candidate.defaultModel;
|
||||||
}
|
}
|
||||||
|
if (typeof candidate.defaultVariant === 'string' && candidate.defaultVariant.length > 0) {
|
||||||
|
result.defaultVariant = candidate.defaultVariant;
|
||||||
|
}
|
||||||
if (typeof candidate.defaultAgent === 'string' && candidate.defaultAgent.length > 0) {
|
if (typeof candidate.defaultAgent === 'string' && candidate.defaultAgent.length > 0) {
|
||||||
result.defaultAgent = candidate.defaultAgent;
|
result.defaultAgent = candidate.defaultAgent;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,6 +160,24 @@ export async function createWorktreeSession(): Promise<{ id: string } | null> {
|
|||||||
useContextStore.getState().saveSessionModelSelection(session.id, providerId, modelId);
|
useContextStore.getState().saveSessionModelSelection(session.id, providerId, modelId);
|
||||||
// Also save the specific agent's model preference for this session
|
// Also save the specific agent's model preference for this session
|
||||||
useContextStore.getState().saveAgentModelForSession(session.id, agentName, providerId, modelId);
|
useContextStore.getState().saveAgentModelForSession(session.id, agentName, providerId, modelId);
|
||||||
|
|
||||||
|
// Seed default variant into session context so ModelControls restore logic
|
||||||
|
// doesn't wipe it on first switch to the new session.
|
||||||
|
const settingsDefaultVariant = configState.settingsDefaultVariant;
|
||||||
|
if (settingsDefaultVariant) {
|
||||||
|
const provider = configState.providers.find((p) => p.id === providerId);
|
||||||
|
const model = provider?.models.find((m: Record<string, unknown>) => (m as { id?: string }).id === modelId) as
|
||||||
|
| { variants?: Record<string, unknown> }
|
||||||
|
| undefined;
|
||||||
|
const variants = model?.variants;
|
||||||
|
|
||||||
|
if (variants && Object.prototype.hasOwnProperty.call(variants, settingsDefaultVariant)) {
|
||||||
|
configState.setCurrentVariant(settingsDefaultVariant);
|
||||||
|
useContextStore
|
||||||
|
.getState()
|
||||||
|
.saveAgentModelVariantForSession(session.id, agentName, providerId, modelId, settingsDefaultVariant);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const FALLBACK_MODEL_ID = "big-pickle";
|
|||||||
|
|
||||||
interface OpenChamberDefaults {
|
interface OpenChamberDefaults {
|
||||||
defaultModel?: string;
|
defaultModel?: string;
|
||||||
|
defaultVariant?: string;
|
||||||
defaultAgent?: string;
|
defaultAgent?: string;
|
||||||
autoCreateWorktree?: boolean;
|
autoCreateWorktree?: boolean;
|
||||||
}
|
}
|
||||||
@@ -33,6 +34,7 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
|||||||
const settings = await getDesktopSettings();
|
const settings = await getDesktopSettings();
|
||||||
return {
|
return {
|
||||||
defaultModel: settings?.defaultModel,
|
defaultModel: settings?.defaultModel,
|
||||||
|
defaultVariant: settings?.defaultVariant,
|
||||||
defaultAgent: settings?.defaultAgent,
|
defaultAgent: settings?.defaultAgent,
|
||||||
autoCreateWorktree: settings?.autoCreateWorktree,
|
autoCreateWorktree: settings?.autoCreateWorktree,
|
||||||
};
|
};
|
||||||
@@ -45,9 +47,14 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
|||||||
const result = await runtimeSettings.load();
|
const result = await runtimeSettings.load();
|
||||||
const data = result?.settings;
|
const data = result?.settings;
|
||||||
if (data) {
|
if (data) {
|
||||||
|
const defaultModel = typeof data?.defaultModel === 'string' ? data.defaultModel.trim() : '';
|
||||||
|
const defaultVariant = typeof data?.defaultVariant === 'string' ? data.defaultVariant.trim() : '';
|
||||||
|
const defaultAgent = typeof data?.defaultAgent === 'string' ? data.defaultAgent.trim() : '';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
defaultModel: typeof data?.defaultModel === 'string' ? data.defaultModel : undefined,
|
defaultModel: defaultModel.length > 0 ? defaultModel : undefined,
|
||||||
defaultAgent: typeof data?.defaultAgent === 'string' ? data.defaultAgent : undefined,
|
defaultVariant: defaultVariant.length > 0 ? defaultVariant : undefined,
|
||||||
|
defaultAgent: defaultAgent.length > 0 ? defaultAgent : undefined,
|
||||||
autoCreateWorktree: typeof data?.autoCreateWorktree === 'boolean' ? data.autoCreateWorktree : undefined,
|
autoCreateWorktree: typeof data?.autoCreateWorktree === 'boolean' ? data.autoCreateWorktree : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -65,9 +72,14 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
const defaultModel = typeof data?.defaultModel === 'string' ? data.defaultModel.trim() : '';
|
||||||
|
const defaultVariant = typeof data?.defaultVariant === 'string' ? data.defaultVariant.trim() : '';
|
||||||
|
const defaultAgent = typeof data?.defaultAgent === 'string' ? data.defaultAgent.trim() : '';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
defaultModel: typeof data?.defaultModel === 'string' ? data.defaultModel : undefined,
|
defaultModel: defaultModel.length > 0 ? defaultModel : undefined,
|
||||||
defaultAgent: typeof data?.defaultAgent === 'string' ? data.defaultAgent : undefined,
|
defaultVariant: defaultVariant.length > 0 ? defaultVariant : undefined,
|
||||||
|
defaultAgent: defaultAgent.length > 0 ? defaultAgent : undefined,
|
||||||
autoCreateWorktree: typeof data?.autoCreateWorktree === 'boolean' ? data.autoCreateWorktree : undefined,
|
autoCreateWorktree: typeof data?.autoCreateWorktree === 'boolean' ? data.autoCreateWorktree : undefined,
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
@@ -326,10 +338,12 @@ const resolveInitialDirectoryKey = (): string => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface DirectoryScopedConfig {
|
interface DirectoryScopedConfig {
|
||||||
|
|
||||||
providers: ProviderWithModelList[];
|
providers: ProviderWithModelList[];
|
||||||
agents: Agent[];
|
agents: Agent[];
|
||||||
currentProviderId: string;
|
currentProviderId: string;
|
||||||
currentModelId: string;
|
currentModelId: string;
|
||||||
|
currentVariant?: string | undefined;
|
||||||
currentAgentName: string | undefined;
|
currentAgentName: string | undefined;
|
||||||
selectedProviderId: string;
|
selectedProviderId: string;
|
||||||
agentModelSelections: { [agentName: string]: { providerId: string; modelId: string } };
|
agentModelSelections: { [agentName: string]: { providerId: string; modelId: string } };
|
||||||
@@ -355,6 +369,7 @@ interface ConfigStore {
|
|||||||
modelsMetadata: Map<string, ModelMetadata>;
|
modelsMetadata: Map<string, ModelMetadata>;
|
||||||
// OpenChamber settings-based defaults (take precedence over agent preferences)
|
// OpenChamber settings-based defaults (take precedence over agent preferences)
|
||||||
settingsDefaultModel: string | undefined; // format: "provider/model"
|
settingsDefaultModel: string | undefined; // format: "provider/model"
|
||||||
|
settingsDefaultVariant: string | undefined;
|
||||||
settingsDefaultAgent: string | undefined;
|
settingsDefaultAgent: string | undefined;
|
||||||
settingsAutoCreateWorktree: boolean;
|
settingsAutoCreateWorktree: boolean;
|
||||||
|
|
||||||
@@ -370,6 +385,7 @@ interface ConfigStore {
|
|||||||
setAgent: (agentName: string | undefined) => void;
|
setAgent: (agentName: string | undefined) => void;
|
||||||
setSelectedProvider: (providerId: string) => void;
|
setSelectedProvider: (providerId: string) => void;
|
||||||
setSettingsDefaultModel: (model: string | undefined) => void;
|
setSettingsDefaultModel: (model: string | undefined) => void;
|
||||||
|
setSettingsDefaultVariant: (variant: string | undefined) => void;
|
||||||
setSettingsDefaultAgent: (agent: string | undefined) => void;
|
setSettingsDefaultAgent: (agent: string | undefined) => void;
|
||||||
setSettingsAutoCreateWorktree: (enabled: boolean) => void;
|
setSettingsAutoCreateWorktree: (enabled: boolean) => void;
|
||||||
saveAgentModelSelection: (agentName: string, providerId: string, modelId: string) => void;
|
saveAgentModelSelection: (agentName: string, providerId: string, modelId: string) => void;
|
||||||
@@ -412,6 +428,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
isInitialized: false,
|
isInitialized: false,
|
||||||
modelsMetadata: new Map<string, ModelMetadata>(),
|
modelsMetadata: new Map<string, ModelMetadata>(),
|
||||||
settingsDefaultModel: undefined,
|
settingsDefaultModel: undefined,
|
||||||
|
settingsDefaultVariant: undefined,
|
||||||
settingsDefaultAgent: undefined,
|
settingsDefaultAgent: undefined,
|
||||||
settingsAutoCreateWorktree: false,
|
settingsAutoCreateWorktree: false,
|
||||||
|
|
||||||
@@ -427,6 +444,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
agents: snapshot.agents,
|
agents: snapshot.agents,
|
||||||
currentProviderId: snapshot.currentProviderId,
|
currentProviderId: snapshot.currentProviderId,
|
||||||
currentModelId: snapshot.currentModelId,
|
currentModelId: snapshot.currentModelId,
|
||||||
|
currentVariant: snapshot.currentVariant,
|
||||||
currentAgentName: snapshot.currentAgentName,
|
currentAgentName: snapshot.currentAgentName,
|
||||||
selectedProviderId: snapshot.selectedProviderId,
|
selectedProviderId: snapshot.selectedProviderId,
|
||||||
agentModelSelections: snapshot.agentModelSelections,
|
agentModelSelections: snapshot.agentModelSelections,
|
||||||
@@ -564,14 +582,14 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
setProvider: (providerId: string) => {
|
setProvider: (providerId: string) => {
|
||||||
const { providers } = get();
|
const { providers } = get();
|
||||||
const provider = providers.find((p) => p.id === providerId);
|
const provider = providers.find((p) => p.id === providerId);
|
||||||
|
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstModel = provider.models[0];
|
const firstModel = provider.models[0];
|
||||||
const newModelId = firstModel?.id || "";
|
const newModelId = firstModel?.id || "";
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const directoryKey = state.activeDirectoryKey;
|
const directoryKey = state.activeDirectoryKey;
|
||||||
const baseSnapshot: DirectoryScopedConfig = state.directoryScoped[directoryKey] ?? {
|
const baseSnapshot: DirectoryScopedConfig = state.directoryScoped[directoryKey] ?? {
|
||||||
@@ -579,6 +597,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
agents: state.agents,
|
agents: state.agents,
|
||||||
currentProviderId: state.currentProviderId,
|
currentProviderId: state.currentProviderId,
|
||||||
currentModelId: state.currentModelId,
|
currentModelId: state.currentModelId,
|
||||||
|
currentVariant: state.currentVariant,
|
||||||
currentAgentName: state.currentAgentName,
|
currentAgentName: state.currentAgentName,
|
||||||
selectedProviderId: state.selectedProviderId,
|
selectedProviderId: state.selectedProviderId,
|
||||||
agentModelSelections: state.agentModelSelections,
|
agentModelSelections: state.agentModelSelections,
|
||||||
@@ -612,6 +631,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
agents: state.agents,
|
agents: state.agents,
|
||||||
currentProviderId: state.currentProviderId,
|
currentProviderId: state.currentProviderId,
|
||||||
currentModelId: state.currentModelId,
|
currentModelId: state.currentModelId,
|
||||||
|
currentVariant: state.currentVariant,
|
||||||
currentAgentName: state.currentAgentName,
|
currentAgentName: state.currentAgentName,
|
||||||
selectedProviderId: state.selectedProviderId,
|
selectedProviderId: state.selectedProviderId,
|
||||||
agentModelSelections: state.agentModelSelections,
|
agentModelSelections: state.agentModelSelections,
|
||||||
@@ -638,7 +658,32 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
if (state.currentVariant === variant) {
|
if (state.currentVariant === variant) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
return { currentVariant: variant };
|
|
||||||
|
const directoryKey = state.activeDirectoryKey;
|
||||||
|
const baseSnapshot: DirectoryScopedConfig = state.directoryScoped[directoryKey] ?? {
|
||||||
|
providers: state.providers,
|
||||||
|
agents: state.agents,
|
||||||
|
currentProviderId: state.currentProviderId,
|
||||||
|
currentModelId: state.currentModelId,
|
||||||
|
currentVariant: state.currentVariant,
|
||||||
|
currentAgentName: state.currentAgentName,
|
||||||
|
selectedProviderId: state.selectedProviderId,
|
||||||
|
agentModelSelections: state.agentModelSelections,
|
||||||
|
defaultProviders: state.defaultProviders,
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextSnapshot: DirectoryScopedConfig = {
|
||||||
|
...baseSnapshot,
|
||||||
|
currentVariant: variant,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentVariant: variant,
|
||||||
|
directoryScoped: {
|
||||||
|
...state.directoryScoped,
|
||||||
|
[directoryKey]: nextSnapshot,
|
||||||
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -659,18 +704,17 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
|
|
||||||
const current = get().currentVariant;
|
const current = get().currentVariant;
|
||||||
if (!current) {
|
if (!current) {
|
||||||
set((state) => (state.currentVariant === variantKeys[0] ? state : { currentVariant: variantKeys[0] }));
|
get().setCurrentVariant(variantKeys[0]);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = variantKeys.indexOf(current);
|
|
||||||
if (index === -1 || index === variantKeys.length - 1) {
|
|
||||||
set((state) => (state.currentVariant === undefined ? state : { currentVariant: undefined }));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextVariant = variantKeys[index + 1];
|
const index = variantKeys.indexOf(current);
|
||||||
set((state) => (state.currentVariant === nextVariant ? state : { currentVariant: nextVariant }));
|
if (index === -1 || index === variantKeys.length - 1) {
|
||||||
|
get().setCurrentVariant(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
get().setCurrentVariant(variantKeys[index + 1]);
|
||||||
},
|
},
|
||||||
|
|
||||||
setSelectedProvider: (providerId: string) => {
|
setSelectedProvider: (providerId: string) => {
|
||||||
@@ -780,9 +824,10 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const nextState: Partial<ConfigStore> = {
|
const nextState: Partial<ConfigStore> = {
|
||||||
settingsDefaultModel: openChamberDefaults.defaultModel,
|
settingsDefaultModel: openChamberDefaults.defaultModel,
|
||||||
settingsDefaultAgent: openChamberDefaults.defaultAgent,
|
settingsDefaultVariant: openChamberDefaults.defaultVariant,
|
||||||
settingsAutoCreateWorktree: openChamberDefaults.autoCreateWorktree ?? false,
|
settingsDefaultAgent: openChamberDefaults.defaultAgent,
|
||||||
|
settingsAutoCreateWorktree: openChamberDefaults.autoCreateWorktree ?? false,
|
||||||
directoryScoped: {
|
directoryScoped: {
|
||||||
...state.directoryScoped,
|
...state.directoryScoped,
|
||||||
[directoryKey]: nextSnapshot,
|
[directoryKey]: nextSnapshot,
|
||||||
@@ -801,9 +846,10 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
const baseSnapshot: DirectoryScopedConfig = state.directoryScoped[directoryKey] ?? {
|
const baseSnapshot: DirectoryScopedConfig = state.directoryScoped[directoryKey] ?? {
|
||||||
providers,
|
providers,
|
||||||
agents: [],
|
agents: [],
|
||||||
currentProviderId: "",
|
currentProviderId: "",
|
||||||
currentModelId: "",
|
currentModelId: "",
|
||||||
currentAgentName: undefined,
|
currentVariant: undefined,
|
||||||
|
currentAgentName: undefined,
|
||||||
selectedProviderId: "",
|
selectedProviderId: "",
|
||||||
agentModelSelections: {},
|
agentModelSelections: {},
|
||||||
defaultProviders: {},
|
defaultProviders: {},
|
||||||
@@ -849,7 +895,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
let resolvedAgent: Agent = fallbackAgent;
|
let resolvedAgent: Agent = fallbackAgent;
|
||||||
|
|
||||||
// Track invalid settings to clear
|
// Track invalid settings to clear
|
||||||
const invalidSettings: { defaultModel?: string; defaultAgent?: string } = {};
|
const invalidSettings: { defaultModel?: string; defaultVariant?: string; defaultAgent?: string } = {};
|
||||||
|
|
||||||
// 1. Check OpenChamber settings for default agent
|
// 1. Check OpenChamber settings for default agent
|
||||||
if (openChamberDefaults.defaultAgent) {
|
if (openChamberDefaults.defaultAgent) {
|
||||||
@@ -862,22 +908,34 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Model Selection ---
|
// --- Model Selection ---
|
||||||
// Priority: settings.defaultModel → agent's preferred model → opencode/big-pickle
|
// Priority: settings.defaultModel → agent's preferred model → opencode/big-pickle
|
||||||
let resolvedProviderId: string | undefined;
|
let resolvedProviderId: string | undefined;
|
||||||
let resolvedModelId: string | undefined;
|
let resolvedModelId: string | undefined;
|
||||||
|
let resolvedVariant: string | undefined;
|
||||||
|
|
||||||
// 1. Check OpenChamber settings for default model
|
// 1. Check OpenChamber settings for default model
|
||||||
if (openChamberDefaults.defaultModel) {
|
if (openChamberDefaults.defaultModel) {
|
||||||
const parsed = parseModelString(openChamberDefaults.defaultModel);
|
const parsed = parseModelString(openChamberDefaults.defaultModel);
|
||||||
if (parsed && validateModel(parsed.providerId, parsed.modelId)) {
|
if (parsed && validateModel(parsed.providerId, parsed.modelId)) {
|
||||||
resolvedProviderId = parsed.providerId;
|
resolvedProviderId = parsed.providerId;
|
||||||
resolvedModelId = parsed.modelId;
|
resolvedModelId = parsed.modelId;
|
||||||
} else {
|
|
||||||
// Model no longer exists - mark for clearing
|
if (openChamberDefaults.defaultVariant) {
|
||||||
invalidSettings.defaultModel = '';
|
const provider = providers.find((p) => p.id === parsed.providerId);
|
||||||
}
|
const model = provider?.models.find((m) => m.id === parsed.modelId) as { variants?: Record<string, unknown> } | undefined;
|
||||||
}
|
const variants = model?.variants;
|
||||||
|
if (variants && Object.prototype.hasOwnProperty.call(variants, openChamberDefaults.defaultVariant)) {
|
||||||
|
resolvedVariant = openChamberDefaults.defaultVariant;
|
||||||
|
} else {
|
||||||
|
invalidSettings.defaultVariant = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Model no longer exists - mark for clearing
|
||||||
|
invalidSettings.defaultModel = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Fall back to agent's preferred model
|
// 2. Fall back to agent's preferred model
|
||||||
if (!resolvedProviderId && resolvedAgent?.model?.providerID && resolvedAgent?.model?.modelID) {
|
if (!resolvedProviderId && resolvedAgent?.model?.providerID && resolvedAgent?.model?.modelID) {
|
||||||
@@ -919,8 +977,9 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
providers,
|
providers,
|
||||||
agents: safeAgents,
|
agents: safeAgents,
|
||||||
currentAgentName: resolvedAgent.name,
|
currentAgentName: resolvedAgent.name,
|
||||||
currentProviderId: resolvedProviderId ?? baseSnapshot.currentProviderId,
|
currentProviderId: resolvedProviderId ?? baseSnapshot.currentProviderId,
|
||||||
currentModelId: resolvedModelId ?? baseSnapshot.currentModelId,
|
currentModelId: resolvedModelId ?? baseSnapshot.currentModelId,
|
||||||
|
currentVariant: resolvedVariant,
|
||||||
};
|
};
|
||||||
|
|
||||||
const nextState: Partial<ConfigStore> = {
|
const nextState: Partial<ConfigStore> = {
|
||||||
@@ -930,13 +989,14 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (state.activeDirectoryKey === directoryKey) {
|
if (state.activeDirectoryKey === directoryKey) {
|
||||||
nextState.currentAgentName = resolvedAgent.name;
|
nextState.currentAgentName = resolvedAgent.name;
|
||||||
if (resolvedProviderId && resolvedModelId) {
|
if (resolvedProviderId && resolvedModelId) {
|
||||||
nextState.currentProviderId = resolvedProviderId;
|
nextState.currentProviderId = resolvedProviderId;
|
||||||
nextState.currentModelId = resolvedModelId;
|
nextState.currentModelId = resolvedModelId;
|
||||||
}
|
nextState.currentVariant = resolvedVariant;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nextState;
|
return nextState;
|
||||||
});
|
});
|
||||||
@@ -944,10 +1004,11 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
// Clear invalid settings from storage (best-effort cleanup)
|
// Clear invalid settings from storage (best-effort cleanup)
|
||||||
if (Object.keys(invalidSettings).length > 0) {
|
if (Object.keys(invalidSettings).length > 0) {
|
||||||
// Also clear from store state
|
// Also clear from store state
|
||||||
set({
|
set({
|
||||||
settingsDefaultModel: invalidSettings.defaultModel !== undefined ? undefined : get().settingsDefaultModel,
|
settingsDefaultModel: invalidSettings.defaultModel !== undefined ? undefined : get().settingsDefaultModel,
|
||||||
settingsDefaultAgent: invalidSettings.defaultAgent !== undefined ? undefined : get().settingsDefaultAgent,
|
settingsDefaultVariant: invalidSettings.defaultVariant !== undefined ? undefined : get().settingsDefaultVariant,
|
||||||
});
|
settingsDefaultAgent: invalidSettings.defaultAgent !== undefined ? undefined : get().settingsDefaultAgent,
|
||||||
|
});
|
||||||
updateDesktopSettings(invalidSettings).catch(() => {
|
updateDesktopSettings(invalidSettings).catch(() => {
|
||||||
// Ignore errors - best effort cleanup
|
// Ignore errors - best effort cleanup
|
||||||
});
|
});
|
||||||
@@ -1003,7 +1064,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
},
|
},
|
||||||
|
|
||||||
setAgent: (agentName: string | undefined) => {
|
setAgent: (agentName: string | undefined) => {
|
||||||
const { agents, providers, settingsDefaultModel } = get();
|
const { agents, providers, settingsDefaultModel, settingsDefaultVariant } = get();
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const directoryKey = state.activeDirectoryKey;
|
const directoryKey = state.activeDirectoryKey;
|
||||||
@@ -1086,21 +1147,34 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
agents: state.agents,
|
agents: state.agents,
|
||||||
currentProviderId: state.currentProviderId,
|
currentProviderId: state.currentProviderId,
|
||||||
currentModelId: state.currentModelId,
|
currentModelId: state.currentModelId,
|
||||||
|
currentVariant: state.currentVariant,
|
||||||
currentAgentName: state.currentAgentName,
|
currentAgentName: state.currentAgentName,
|
||||||
selectedProviderId: state.selectedProviderId,
|
selectedProviderId: state.selectedProviderId,
|
||||||
agentModelSelections: state.agentModelSelections,
|
agentModelSelections: state.agentModelSelections,
|
||||||
defaultProviders: state.defaultProviders,
|
defaultProviders: state.defaultProviders,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let nextVariant: string | undefined;
|
||||||
|
if (settingsDefaultVariant) {
|
||||||
|
const settingsProvider = providers.find((p) => p.id === parsed.providerId);
|
||||||
|
const model = settingsProvider?.models.find((m) => m.id === parsed.modelId) as { variants?: Record<string, unknown> } | undefined;
|
||||||
|
const variants = model?.variants;
|
||||||
|
if (variants && Object.prototype.hasOwnProperty.call(variants, settingsDefaultVariant)) {
|
||||||
|
nextVariant = settingsDefaultVariant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const nextSnapshot: DirectoryScopedConfig = {
|
const nextSnapshot: DirectoryScopedConfig = {
|
||||||
...baseSnapshot,
|
...baseSnapshot,
|
||||||
currentProviderId: parsed.providerId,
|
currentProviderId: parsed.providerId,
|
||||||
currentModelId: parsed.modelId,
|
currentModelId: parsed.modelId,
|
||||||
|
currentVariant: nextVariant,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentProviderId: parsed.providerId,
|
currentProviderId: parsed.providerId,
|
||||||
currentModelId: parsed.modelId,
|
currentModelId: parsed.modelId,
|
||||||
|
currentVariant: nextVariant,
|
||||||
directoryScoped: {
|
directoryScoped: {
|
||||||
...state.directoryScoped,
|
...state.directoryScoped,
|
||||||
[directoryKey]: nextSnapshot,
|
[directoryKey]: nextSnapshot,
|
||||||
@@ -1156,13 +1230,17 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setSettingsDefaultModel: (model: string | undefined) => {
|
setSettingsDefaultModel: (model: string | undefined) => {
|
||||||
set({ settingsDefaultModel: model });
|
set({ settingsDefaultModel: model });
|
||||||
},
|
},
|
||||||
|
|
||||||
setSettingsDefaultAgent: (agent: string | undefined) => {
|
setSettingsDefaultVariant: (variant: string | undefined) => {
|
||||||
set({ settingsDefaultAgent: agent });
|
set({ settingsDefaultVariant: variant });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setSettingsDefaultAgent: (agent: string | undefined) => {
|
||||||
|
set({ settingsDefaultAgent: agent });
|
||||||
|
},
|
||||||
|
|
||||||
setSettingsAutoCreateWorktree: (enabled: boolean) => {
|
setSettingsAutoCreateWorktree: (enabled: boolean) => {
|
||||||
set({ settingsAutoCreateWorktree: enabled });
|
set({ settingsAutoCreateWorktree: enabled });
|
||||||
|
|||||||
@@ -80,6 +80,15 @@ const persistSettings = async (changes: Record<string, unknown>, ctx?: BridgeCon
|
|||||||
const current = readSettings(ctx);
|
const current = readSettings(ctx);
|
||||||
const restChanges = { ...(changes || {}) };
|
const restChanges = { ...(changes || {}) };
|
||||||
delete restChanges.lastDirectory;
|
delete restChanges.lastDirectory;
|
||||||
|
|
||||||
|
// Normalize empty-string clears to key removal (match web/desktop behavior)
|
||||||
|
for (const key of ['defaultModel', 'defaultVariant', 'defaultAgent']) {
|
||||||
|
const value = restChanges[key];
|
||||||
|
if (typeof value === 'string' && value.trim().length === 0) {
|
||||||
|
delete restChanges[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const merged = { ...current, ...restChanges, lastDirectory: current.lastDirectory };
|
const merged = { ...current, ...restChanges, lastDirectory: current.lastDirectory };
|
||||||
await ctx?.context?.globalState.update(SETTINGS_KEY, merged);
|
await ctx?.context?.globalState.update(SETTINGS_KEY, merged);
|
||||||
return merged;
|
return merged;
|
||||||
|
|||||||
@@ -590,11 +590,17 @@ const sanitizeSettingsUpdate = (payload) => {
|
|||||||
result.typographySizes = typography;
|
result.typographySizes = typography;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof candidate.defaultModel === 'string' && candidate.defaultModel.length > 0) {
|
if (typeof candidate.defaultModel === 'string') {
|
||||||
result.defaultModel = candidate.defaultModel;
|
const trimmed = candidate.defaultModel.trim();
|
||||||
|
result.defaultModel = trimmed.length > 0 ? trimmed : undefined;
|
||||||
}
|
}
|
||||||
if (typeof candidate.defaultAgent === 'string' && candidate.defaultAgent.length > 0) {
|
if (typeof candidate.defaultVariant === 'string') {
|
||||||
result.defaultAgent = candidate.defaultAgent;
|
const trimmed = candidate.defaultVariant.trim();
|
||||||
|
result.defaultVariant = trimmed.length > 0 ? trimmed : undefined;
|
||||||
|
}
|
||||||
|
if (typeof candidate.defaultAgent === 'string') {
|
||||||
|
const trimmed = candidate.defaultAgent.trim();
|
||||||
|
result.defaultAgent = trimmed.length > 0 ? trimmed : undefined;
|
||||||
}
|
}
|
||||||
if (typeof candidate.queueModeEnabled === 'boolean') {
|
if (typeof candidate.queueModeEnabled === 'boolean') {
|
||||||
result.queueModeEnabled = candidate.queueModeEnabled;
|
result.queueModeEnabled = candidate.queueModeEnabled;
|
||||||
|
|||||||
Reference in New Issue
Block a user