Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useAuthStateValue } from "@features/auth/hooks/authQueries";
import { SettingRow } from "@features/settings/components/SettingRow";
import {
type AutoConvertLongText,
type CompletionSound,
type DefaultInitialTaskMode,
type DiffOpenMode,
type AutoConvertLongText,
type CompletionSound,
type DefaultInitialTaskMode,
type DefaultReasoningEffort,
type DiffOpenMode,
type SendMessagesWith,
useSettingsStore,
} from "@features/settings/stores/settingsStore";
Expand Down Expand Up @@ -75,19 +76,21 @@ export function GeneralSettings() {
dockBounceNotifications,
completionSound,
completionVolume,
autoConvertLongText,
defaultInitialTaskMode,
diffOpenMode,
sendMessagesWith,
hedgehogMode,
setDesktopNotifications,
setDockBadgeNotifications,
setDockBounceNotifications,
setCompletionSound,
setCompletionVolume,
setAutoConvertLongText,
setDefaultInitialTaskMode,
setDiffOpenMode,
autoConvertLongText,
defaultInitialTaskMode,
defaultReasoningEffort,
diffOpenMode,
sendMessagesWith,
hedgehogMode,
setDesktopNotifications,
setDockBadgeNotifications,
setDockBounceNotifications,
setCompletionSound,
setCompletionVolume,
setAutoConvertLongText,
setDefaultInitialTaskMode,
setDefaultReasoningEffort,
setDiffOpenMode,
setSendMessagesWith,
setHedgehogMode,
} = useSettingsStore();
Expand Down Expand Up @@ -178,19 +181,31 @@ export function GeneralSettings() {
[diffOpenMode, setDiffOpenMode],
);

const handleDefaultInitialTaskModeChange = useCallback(
(value: DefaultInitialTaskMode) => {
track(ANALYTICS_EVENTS.SETTING_CHANGED, {
setting_name: "default_initial_task_mode",
new_value: value,
old_value: defaultInitialTaskMode,
});
setDefaultInitialTaskMode(value);
},
[defaultInitialTaskMode, setDefaultInitialTaskMode],
);

const handleSendMessagesWithChange = useCallback(
const handleDefaultInitialTaskModeChange = useCallback(
(value: DefaultInitialTaskMode) => {
track(ANALYTICS_EVENTS.SETTING_CHANGED, {
setting_name: "default_initial_task_mode",
new_value: value,
old_value: defaultInitialTaskMode,
});
setDefaultInitialTaskMode(value);
},
[defaultInitialTaskMode, setDefaultInitialTaskMode],
);

const handleDefaultReasoningEffortChange = useCallback(
(value: DefaultReasoningEffort) => {
track(ANALYTICS_EVENTS.SETTING_CHANGED, {
setting_name: "default_reasoning_effort",
new_value: value,
old_value: defaultReasoningEffort,
});
setDefaultReasoningEffort(value);
},
[defaultReasoningEffort, setDefaultReasoningEffort],
);

const handleSendMessagesWithChange = useCallback(
(value: SendMessagesWith) => {
track(ANALYTICS_EVENTS.SETTING_CHANGED, {
setting_name: "send_messages_with",
Expand Down Expand Up @@ -381,13 +396,36 @@ export function GeneralSettings() {
<Select.Trigger className="min-w-[100px]" />
<Select.Content>
<Select.Item value="plan">Plan</Select.Item>
<Select.Item value="last_used">Last used</Select.Item>
</Select.Content>
</Select.Root>
</SettingRow>

<SettingRow
label="Send messages with"
<Select.Item value="last_used">Last used</Select.Item>
</Select.Content>
</Select.Root>
</SettingRow>

<SettingRow
label="Default effort level"
description="Choose the default reasoning effort for new tasks, or remember your last-used level"
>
<Select.Root
value={defaultReasoningEffort}
onValueChange={(value) =>
handleDefaultReasoningEffortChange(value as DefaultReasoningEffort)
}
size="1"
>
<Select.Trigger className="min-w-[100px]" />
<Select.Content>
<Select.Item value="last_used">Last used</Select.Item>
<Select.Item value="low">Low</Select.Item>
<Select.Item value="medium">Medium</Select.Item>
<Select.Item value="high">High</Select.Item>
<Select.Item value="xhigh">Extra High</Select.Item>
<Select.Item value="max">Max</Select.Item>
</Select.Content>
</Select.Root>
</SettingRow>

<SettingRow
label="Send messages with"
description="Choose which key combination sends messages. Use Shift+Enter for new lines"
>
<Select.Root
Expand Down
41 changes: 24 additions & 17 deletions apps/code/src/renderer/features/settings/stores/settingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type CompletionSound =
export type AgentAdapter = "claude" | "codex";
export type AutoConvertLongText = "off" | "1000" | "2500" | "5000" | "10000";
export type DefaultInitialTaskMode = "plan" | "last_used";
export type DefaultReasoningEffort = "low" | "medium" | "high" | "xhigh" | "max" | "last_used";

export interface HintState {
count: number;
Expand Down Expand Up @@ -53,9 +54,10 @@ interface SettingsStore {
preventSleepWhileRunning: boolean;
debugLogsCloudRuns: boolean;
customInstructions: string;
defaultInitialTaskMode: DefaultInitialTaskMode;
lastUsedInitialTaskMode: ExecutionMode;
diffOpenMode: DiffOpenMode;
defaultInitialTaskMode: DefaultInitialTaskMode;
lastUsedInitialTaskMode: ExecutionMode;
defaultReasoningEffort: DefaultReasoningEffort;
diffOpenMode: DiffOpenMode;
hedgehogMode: boolean;
mcpAppsDisabledServers: string[];
hints: Record<string, HintState>;
Expand Down Expand Up @@ -89,9 +91,10 @@ interface SettingsStore {
setPreventSleepWhileRunning: (enabled: boolean) => void;
setDebugLogsCloudRuns: (enabled: boolean) => void;
setCustomInstructions: (instructions: string) => void;
setDefaultInitialTaskMode: (mode: DefaultInitialTaskMode) => void;
setLastUsedInitialTaskMode: (mode: ExecutionMode) => void;
setDiffOpenMode: (mode: DiffOpenMode) => void;
setDefaultInitialTaskMode: (mode: DefaultInitialTaskMode) => void;
setLastUsedInitialTaskMode: (mode: ExecutionMode) => void;
setDefaultReasoningEffort: (effort: DefaultReasoningEffort) => void;
setDiffOpenMode: (mode: DiffOpenMode) => void;
setHedgehogMode: (enabled: boolean) => void;
setMcpAppsDisabledServers: (servers: string[]) => void;
}
Expand Down Expand Up @@ -120,9 +123,10 @@ export const useSettingsStore = create<SettingsStore>()(
preventSleepWhileRunning: false,
debugLogsCloudRuns: false,
customInstructions: "",
defaultInitialTaskMode: "plan",
lastUsedInitialTaskMode: "plan",
diffOpenMode: "auto",
defaultInitialTaskMode: "plan",
lastUsedInitialTaskMode: "plan",
defaultReasoningEffort: "last_used",
diffOpenMode: "auto",
hedgehogMode: false,
mcpAppsDisabledServers: [],
hints: {},
Expand Down Expand Up @@ -194,11 +198,13 @@ export const useSettingsStore = create<SettingsStore>()(
setDebugLogsCloudRuns: (enabled) => set({ debugLogsCloudRuns: enabled }),
setCustomInstructions: (instructions) =>
set({ customInstructions: instructions }),
setDefaultInitialTaskMode: (mode) =>
set({ defaultInitialTaskMode: mode }),
setLastUsedInitialTaskMode: (mode) =>
set({ lastUsedInitialTaskMode: mode }),
setDiffOpenMode: (mode) => set({ diffOpenMode: mode }),
setDefaultInitialTaskMode: (mode) =>
set({ defaultInitialTaskMode: mode }),
setLastUsedInitialTaskMode: (mode) =>
set({ lastUsedInitialTaskMode: mode }),
setDefaultReasoningEffort: (effort) =>
set({ defaultReasoningEffort: effort }),
setDiffOpenMode: (mode) => set({ diffOpenMode: mode }),
setHedgehogMode: (enabled) => set({ hedgehogMode: enabled }),
setMcpAppsDisabledServers: (servers) =>
set({ mcpAppsDisabledServers: servers }),
Expand Down Expand Up @@ -228,9 +234,10 @@ export const useSettingsStore = create<SettingsStore>()(
preventSleepWhileRunning: state.preventSleepWhileRunning,
debugLogsCloudRuns: state.debugLogsCloudRuns,
customInstructions: state.customInstructions,
defaultInitialTaskMode: state.defaultInitialTaskMode,
lastUsedInitialTaskMode: state.lastUsedInitialTaskMode,
diffOpenMode: state.diffOpenMode,
defaultInitialTaskMode: state.defaultInitialTaskMode,
lastUsedInitialTaskMode: state.lastUsedInitialTaskMode,
defaultReasoningEffort: state.defaultReasoningEffort,
diffOpenMode: state.diffOpenMode,
hedgehogMode: state.hedgehogMode,
hints: state.hints,
mcpAppsDisabledServers: state.mcpAppsDisabledServers,
Expand Down
103 changes: 62 additions & 41 deletions apps/code/src/renderer/features/task-detail/hooks/usePreviewConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,12 @@ export function usePreviewConfig(
.then((options) => {
if (abort.signal.aborted) return;

const {
defaultInitialTaskMode,
lastUsedInitialTaskMode,
lastUsedReasoningEffort,
} = useSettingsStore.getState();
const {
defaultInitialTaskMode,
lastUsedInitialTaskMode,
defaultReasoningEffort,
lastUsedReasoningEffort,
} = useSettingsStore.getState();

// Use the mode option's existing currentValue (set by the server
// based on the adapter) when the user hasn't chosen a preference,
Expand Down Expand Up @@ -111,27 +112,40 @@ export function usePreviewConfig(
: opt,
);

const withEffort = withMode.map((opt) => {
if (opt.category !== "thought_level" || opt.type !== "select") {
return opt;
}
const validValues = flattenValues(
opt.options as Array<{
value?: string;
options?: Array<{ value: string }>;
}>,
);
if (
lastUsedReasoningEffort &&
validValues.includes(lastUsedReasoningEffort)
) {
return {
...opt,
currentValue: lastUsedReasoningEffort,
} as SessionConfigOption;
}
return opt;
});
const withEffort = withMode.map((opt) => {
if (opt.category !== "thought_level" || opt.type !== "select") {
return opt;
}
const validValues = flattenValues(
opt.options as Array<{
value?: string;
options?: Array<{ value: string }>;
}>,
);
// Mirror the defaultInitialTaskMode pattern: if defaultReasoningEffort
// is "last_used" and we have a valid last-used value, use it;
// otherwise, use the explicit default the user configured.
if (defaultReasoningEffort === "last_used") {
if (
lastUsedReasoningEffort &&
validValues.includes(lastUsedReasoningEffort)
) {
return {
...opt,
currentValue: lastUsedReasoningEffort,
} as SessionConfigOption;
}
return opt;
}
// User chose a fixed default — use it if valid for this model
if (validValues.includes(defaultReasoningEffort)) {
return {
...opt,
currentValue: defaultReasoningEffort,
} as SessionConfigOption;
}
return opt;
});

setConfigOptions(withEffort);
setIsLoading(false);
Expand Down Expand Up @@ -168,26 +182,33 @@ export function usePreviewConfig(
? "reasoning_effort"
: "effort";

const { lastUsedReasoningEffort } = useSettingsStore.getState();
const isValidEffort = (effort: unknown): effort is string =>
typeof effort === "string" &&
!!effortOpts?.some((e) => e.value === effort);
if (effortOpts && existingIdx >= 0) {
const currentEffort = updated[existingIdx].currentValue;
const nextEffort = isValidEffort(currentEffort)
? currentEffort
: isValidEffort(lastUsedReasoningEffort)
? lastUsedReasoningEffort
: "high";
const { lastUsedReasoningEffort, defaultReasoningEffort } =
useSettingsStore.getState();
const isValidEffort = (effort: unknown): effort is string =>
typeof effort === "string" &&
!!effortOpts?.some((e) => e.value === effort);
const resolveEffortFallback = (): string => {
if (defaultReasoningEffort !== "last_used") {
return isValidEffort(defaultReasoningEffort)
? defaultReasoningEffort
: "high";
}
return isValidEffort(lastUsedReasoningEffort)
? lastUsedReasoningEffort
: "high";
};
if (effortOpts && existingIdx >= 0) {
const currentEffort = updated[existingIdx].currentValue;
const nextEffort = isValidEffort(currentEffort)
? currentEffort
: resolveEffortFallback();
updated[existingIdx] = {
...updated[existingIdx],
currentValue: nextEffort,
options: effortOpts,
} as SessionConfigOption;
} else if (effortOpts && existingIdx === -1) {
const nextEffort = isValidEffort(lastUsedReasoningEffort)
? lastUsedReasoningEffort
: "high";
} else if (effortOpts && existingIdx === -1) {
const nextEffort = resolveEffortFallback();
updated = [
...updated,
{
Expand Down