feat: add Claude and Codex effort controls

This commit is contained in:
Simos Mikelatos
2026-06-30 23:29:57 +00:00
parent 2ebe64f218
commit d618abb075
12 changed files with 447 additions and 42 deletions

View File

@@ -16,6 +16,10 @@ import ChatMessagesPane from './subcomponents/ChatMessagesPane';
import ChatComposer from './subcomponents/ChatComposer';
import CommandResultModal from './subcomponents/CommandResultModal';
const FALLBACK_COMPOSER_EFFORT_OPTIONS = {
claude: ['low', 'medium', 'high', 'xhigh', 'max'].map((value) => ({ value })),
codex: ['low', 'medium', 'high', 'xhigh'].map((value) => ({ value })),
} as const;
function ChatInterface({
selectedProject,
@@ -71,6 +75,10 @@ function ChatInterface({
setClaudeModel,
codexModel,
setCodexModel,
claudeEffort,
setClaudeEffort,
codexEffort,
setCodexEffort,
geminiModel,
setGeminiModel,
opencodeModel,
@@ -85,6 +93,7 @@ function ChatInterface({
providerModelsRefreshing,
hardRefreshProviderModels,
selectProviderModel,
setStoredProviderEffort,
} = useChatProviderState({
selectedSession,
selectedProject,
@@ -199,6 +208,8 @@ function ChatInterface({
cursorModel,
claudeModel,
codexModel,
claudeEffort,
codexEffort,
geminiModel,
opencodeModel,
isLoading: isProcessing,
@@ -217,6 +228,37 @@ function ChatInterface({
setPendingPermissionRequests,
});
const currentComposerModel = useMemo(() => {
if (provider === 'claude') return claudeModel;
if (provider === 'codex') return codexModel;
if (provider === 'gemini') return geminiModel;
if (provider === 'opencode') return opencodeModel;
return cursorModel;
}, [provider, claudeModel, codexModel, geminiModel, opencodeModel, cursorModel]);
const composerEffort = useMemo(() => {
if (provider === 'claude') return claudeEffort;
if (provider === 'codex') return codexEffort;
return 'default';
}, [provider, claudeEffort, codexEffort]);
const composerEffortOptions = useMemo(() => {
const modelOption = providerModelCatalog[provider]?.OPTIONS.find((option) => option.value === currentComposerModel);
if (modelOption?.effort?.values?.length) {
return modelOption.effort.values;
}
if (provider === 'claude') {
return FALLBACK_COMPOSER_EFFORT_OPTIONS.claude;
}
if (provider === 'codex') {
return FALLBACK_COMPOSER_EFFORT_OPTIONS.codex;
}
return [];
}, [providerModelCatalog, provider, currentComposerModel]);
// On WebSocket reconnect, re-fetch the current session's messages from the
// server so missed streaming events are shown, then re-subscribe — the
// `chat_subscribed` ack restores or clears the activity indicator, replays
@@ -371,6 +413,9 @@ function ChatInterface({
onAbortSession={handleAbortSession}
permissionMode={permissionMode}
onModeSwitch={cyclePermissionMode}
effort={composerEffort}
availableEffortOptions={composerEffortOptions}
onSelectEffort={(nextEffort) => setStoredProviderEffort(provider, nextEffort)}
tokenBudget={tokenBudget}
onShowTokenUsage={showCostModal}
slashCommandsCount={slashCommandsCount}