diff --git a/apps/code/src/renderer/features/settings/components/sections/GeneralSettings.tsx b/apps/code/src/renderer/features/settings/components/sections/GeneralSettings.tsx
index c9d35b912..c64480e83 100644
--- a/apps/code/src/renderer/features/settings/components/sections/GeneralSettings.tsx
+++ b/apps/code/src/renderer/features/settings/components/sections/GeneralSettings.tsx
@@ -4,6 +4,7 @@ import {
type AutoConvertLongText,
type CompletionSound,
type DefaultInitialTaskMode,
+ type DefaultReasoningEffort,
type DiffOpenMode,
type SendMessagesWith,
useSettingsStore,
@@ -77,6 +78,7 @@ export function GeneralSettings() {
completionVolume,
autoConvertLongText,
defaultInitialTaskMode,
+ defaultReasoningEffort,
diffOpenMode,
sendMessagesWith,
hedgehogMode,
@@ -87,6 +89,7 @@ export function GeneralSettings() {
setCompletionVolume,
setAutoConvertLongText,
setDefaultInitialTaskMode,
+ setDefaultReasoningEffort,
setDiffOpenMode,
setSendMessagesWith,
setHedgehogMode,
@@ -190,6 +193,18 @@ export function GeneralSettings() {
[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, {
@@ -386,6 +401,29 @@ export function GeneralSettings() {
+
+
+ handleDefaultReasoningEffortChange(value as DefaultReasoningEffort)
+ }
+ size="1"
+ >
+
+
+ Last used
+ Low
+ Medium
+ High
+ Extra High
+ Max
+
+
+
+
;
defaultInitialTaskMode: DefaultInitialTaskMode;
lastUsedInitialTaskMode: ExecutionMode;
+ defaultReasoningEffort: DefaultReasoningEffort;
setDefaultRunMode: (mode: DefaultRunMode) => void;
setLastUsedRunMode: (mode: "local" | "cloud") => void;
setLastUsedLocalWorkspaceMode: (mode: LocalWorkspaceMode) => void;
@@ -71,6 +79,7 @@ interface SettingsStore {
getLastUsedEnvironment: (repoPath: string) => string | null;
setDefaultInitialTaskMode: (mode: DefaultInitialTaskMode) => void;
setLastUsedInitialTaskMode: (mode: ExecutionMode) => void;
+ setDefaultReasoningEffort: (effort: DefaultReasoningEffort) => void;
// Notifications
desktopNotifications: boolean;
@@ -140,6 +149,7 @@ export const useSettingsStore = create()(
lastUsedEnvironments: {},
defaultInitialTaskMode: "plan",
lastUsedInitialTaskMode: "plan",
+ defaultReasoningEffort: "last_used",
setDefaultRunMode: (mode) => set({ defaultRunMode: mode }),
setLastUsedRunMode: (mode) => set({ lastUsedRunMode: mode }),
setLastUsedLocalWorkspaceMode: (mode) =>
@@ -167,6 +177,8 @@ export const useSettingsStore = create()(
set({ defaultInitialTaskMode: mode }),
setLastUsedInitialTaskMode: (mode) =>
set({ lastUsedInitialTaskMode: mode }),
+ setDefaultReasoningEffort: (effort) =>
+ set({ defaultReasoningEffort: effort }),
// Notifications
desktopNotifications: true,
@@ -264,6 +276,7 @@ export const useSettingsStore = create()(
lastUsedEnvironments: state.lastUsedEnvironments,
defaultInitialTaskMode: state.defaultInitialTaskMode,
lastUsedInitialTaskMode: state.lastUsedInitialTaskMode,
+ defaultReasoningEffort: state.defaultReasoningEffort,
// Notifications
desktopNotifications: state.desktopNotifications,
diff --git a/apps/code/src/renderer/features/task-detail/hooks/usePreviewConfig.ts b/apps/code/src/renderer/features/task-detail/hooks/usePreviewConfig.ts
index 02a0aa2d0..94a6dfbe7 100644
--- a/apps/code/src/renderer/features/task-detail/hooks/usePreviewConfig.ts
+++ b/apps/code/src/renderer/features/task-detail/hooks/usePreviewConfig.ts
@@ -35,6 +35,45 @@ function flattenValues(
);
}
+const EFFORT_RANK: Record = {
+ low: 0,
+ medium: 1,
+ high: 2,
+ xhigh: 3,
+ max: 4,
+};
+
+/**
+ * Clamp a desired effort to the nearest level the current model supports.
+ * Falls back to the highest supported level when the desired level has no
+ * known rank (e.g. unrecognized value from older settings).
+ */
+function clampEffortToAvailable(
+ desired: string,
+ available: string[],
+): string | null {
+ if (available.length === 0) return null;
+ if (available.includes(desired)) return desired;
+
+ const desiredRank = EFFORT_RANK[desired];
+ if (desiredRank === undefined) {
+ return available[available.length - 1];
+ }
+
+ const ranked = available
+ .map((value) => ({ value, rank: EFFORT_RANK[value] }))
+ .filter((entry): entry is { value: string; rank: number } =>
+ Number.isFinite(entry.rank),
+ );
+ if (ranked.length === 0) return available[0];
+
+ return ranked.reduce((closest, entry) =>
+ Math.abs(entry.rank - desiredRank) < Math.abs(closest.rank - desiredRank)
+ ? entry
+ : closest,
+ ).value;
+}
+
/**
* Fetches config options (models, modes, effort levels) for the task input
* page via a lightweight tRPC query. No agent session is created.
@@ -70,6 +109,7 @@ export function usePreviewConfig(
const {
defaultInitialTaskMode,
lastUsedInitialTaskMode,
+ defaultReasoningEffort,
lastUsedReasoningEffort,
} = useSettingsStore.getState();
@@ -121,13 +161,26 @@ export function usePreviewConfig(
options?: Array<{ value: string }>;
}>,
);
- if (
- lastUsedReasoningEffort &&
- validValues.includes(lastUsedReasoningEffort)
- ) {
+ if (defaultReasoningEffort === "last_used") {
+ if (
+ lastUsedReasoningEffort &&
+ validValues.includes(lastUsedReasoningEffort)
+ ) {
+ return {
+ ...opt,
+ currentValue: lastUsedReasoningEffort,
+ } as SessionConfigOption;
+ }
+ return opt;
+ }
+ const clamped = clampEffortToAvailable(
+ defaultReasoningEffort,
+ validValues,
+ );
+ if (clamped) {
return {
...opt,
- currentValue: lastUsedReasoningEffort,
+ currentValue: clamped,
} as SessionConfigOption;
}
return opt;
@@ -168,26 +221,34 @@ export function usePreviewConfig(
? "reasoning_effort"
: "effort";
- const { lastUsedReasoningEffort } = useSettingsStore.getState();
+ 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" &&
+ isValidEffort(defaultReasoningEffort)
+ ) {
+ return defaultReasoningEffort;
+ }
+ return isValidEffort(lastUsedReasoningEffort)
+ ? lastUsedReasoningEffort
+ : "high";
+ };
if (effortOpts && existingIdx >= 0) {
const currentEffort = updated[existingIdx].currentValue;
const nextEffort = isValidEffort(currentEffort)
? currentEffort
- : isValidEffort(lastUsedReasoningEffort)
- ? lastUsedReasoningEffort
- : "high";
+ : resolveEffortFallback();
updated[existingIdx] = {
...updated[existingIdx],
currentValue: nextEffort,
options: effortOpts,
} as SessionConfigOption;
} else if (effortOpts && existingIdx === -1) {
- const nextEffort = isValidEffort(lastUsedReasoningEffort)
- ? lastUsedReasoningEffort
- : "high";
+ const nextEffort = resolveEffortFallback();
updated = [
...updated,
{