feat: add codex reasoning effort option (#297)

This commit is contained in:
ROOOO
2026-03-17 22:55:55 +08:00
committed by GitHub
parent caa76826f4
commit cb09f0b898
18 changed files with 107 additions and 6 deletions
+2 -1
View File
@@ -381,13 +381,14 @@ export class ApiClient {
directory: string,
agent?: 'claude' | 'codex' | 'cursor' | 'gemini' | 'opencode',
model?: string,
modelReasoningEffort?: string,
yolo?: boolean,
sessionType?: 'simple' | 'worktree',
worktreeName?: string
): Promise<SpawnResponse> {
return await this.request<SpawnResponse>(`/api/machines/${encodeURIComponent(machineId)}/spawn`, {
method: 'POST',
body: JSON.stringify({ directory, agent, model, yolo, sessionType, worktreeName })
body: JSON.stringify({ directory, agent, model, modelReasoningEffort, yolo, sessionType, worktreeName })
})
}
@@ -0,0 +1,37 @@
import type { AgentType, CodexReasoningEffort } from './types'
import { CODEX_REASONING_EFFORT_OPTIONS } from './types'
import { useTranslation } from '@/lib/use-translation'
export function ReasoningEffortSelector(props: {
agent: AgentType
value: CodexReasoningEffort
isDisabled: boolean
onChange: (value: CodexReasoningEffort) => void
}) {
const { t } = useTranslation()
if (props.agent !== 'codex') {
return null
}
return (
<div className="flex flex-col gap-1.5 px-3 py-3">
<label className="text-xs font-medium text-[var(--app-hint)]">
{t('newSession.reasoningEffort')}{' '}
<span className="font-normal">({t('newSession.model.optional')})</span>
</label>
<select
value={props.value}
onChange={(e) => props.onChange(e.target.value as CodexReasoningEffort)}
disabled={props.isDisabled}
className="w-full px-3 py-2 text-sm rounded-lg border border-[var(--app-divider)] bg-[var(--app-bg)] text-[var(--app-text)] focus:outline-none focus:ring-2 focus:ring-[var(--app-link)] disabled:opacity-50"
>
{CODEX_REASONING_EFFORT_OPTIONS.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
)
}
+13 -1
View File
@@ -7,12 +7,13 @@ import { useSessions } from '@/hooks/queries/useSessions'
import { useActiveSuggestions, type Suggestion } from '@/hooks/useActiveSuggestions'
import { useDirectorySuggestions } from '@/hooks/useDirectorySuggestions'
import { useRecentPaths } from '@/hooks/useRecentPaths'
import type { AgentType, SessionType } from './types'
import type { AgentType, CodexReasoningEffort, SessionType } from './types'
import { ActionButtons } from './ActionButtons'
import { AgentSelector } from './AgentSelector'
import { DirectorySection } from './DirectorySection'
import { MachineSelector } from './MachineSelector'
import { ModelSelector } from './ModelSelector'
import { ReasoningEffortSelector } from './ReasoningEffortSelector'
import {
loadPreferredAgent,
loadPreferredYoloMode,
@@ -43,6 +44,7 @@ export function NewSession(props: {
const [pathExistence, setPathExistence] = useState<Record<string, boolean>>({})
const [agent, setAgent] = useState<AgentType>(loadPreferredAgent)
const [model, setModel] = useState('auto')
const [modelReasoningEffort, setModelReasoningEffort] = useState<CodexReasoningEffort>('default')
const [yoloMode, setYoloMode] = useState(loadPreferredYoloMode)
const [sessionType, setSessionType] = useState<SessionType>('simple')
const [worktreeName, setWorktreeName] = useState('')
@@ -220,11 +222,15 @@ export function NewSession(props: {
setError(null)
try {
const resolvedModel = model !== 'auto' && agent !== 'opencode' ? model : undefined
const resolvedModelReasoningEffort = agent === 'codex' && modelReasoningEffort !== 'default'
? modelReasoningEffort
: undefined
const result = await spawnSession({
machineId,
directory: directory.trim(),
agent,
model: resolvedModel,
modelReasoningEffort: resolvedModelReasoningEffort,
yolo: yoloMode,
sessionType,
worktreeName: sessionType === 'worktree' ? (worktreeName.trim() || undefined) : undefined
@@ -294,6 +300,12 @@ export function NewSession(props: {
isDisabled={isFormDisabled}
onModelChange={setModel}
/>
<ReasoningEffortSelector
agent={agent}
value={modelReasoningEffort}
isDisabled={isFormDisabled}
onChange={setModelReasoningEffort}
/>
<YoloToggle
yoloMode={yoloMode}
isDisabled={isFormDisabled}
+9
View File
@@ -1,5 +1,6 @@
export type AgentType = 'claude' | 'codex' | 'cursor' | 'gemini' | 'opencode'
export type SessionType = 'simple' | 'worktree'
export type CodexReasoningEffort = 'default' | 'low' | 'medium' | 'high' | 'xhigh'
export const MODEL_OPTIONS: Record<AgentType, { value: string; label: string }[]> = {
claude: [
@@ -27,3 +28,11 @@ export const MODEL_OPTIONS: Record<AgentType, { value: string; label: string }[]
],
opencode: [],
}
export const CODEX_REASONING_EFFORT_OPTIONS: { value: CodexReasoningEffort; label: string }[] = [
{ value: 'default', label: 'Default' },
{ value: 'low', label: 'Low' },
{ value: 'medium', label: 'Medium' },
{ value: 'high', label: 'High' },
{ value: 'xhigh', label: 'XHigh' },
]
@@ -8,6 +8,7 @@ type SpawnInput = {
directory: string
agent?: 'claude' | 'codex' | 'cursor' | 'gemini' | 'opencode'
model?: string
modelReasoningEffort?: string
yolo?: boolean
sessionType?: 'simple' | 'worktree'
worktreeName?: string
@@ -30,6 +31,7 @@ export function useSpawnSession(api: ApiClient | null): {
input.directory,
input.agent,
input.model,
input.modelReasoningEffort,
input.yolo,
input.sessionType,
input.worktreeName
+1
View File
@@ -104,6 +104,7 @@ export default {
'newSession.agent': 'Agent',
'newSession.model': 'Model',
'newSession.model.optional': 'optional',
'newSession.reasoningEffort': 'Reasoning effort',
'newSession.yolo': 'YOLO mode',
'newSession.yolo.title': 'Bypass approvals and sandbox',
'newSession.yolo.desc': 'Uses dangerous agent flags when spawning.',
+1
View File
@@ -106,6 +106,7 @@ export default {
'newSession.agent': '代理',
'newSession.model': '模型',
'newSession.model.optional': '可选',
'newSession.reasoningEffort': '推理强度',
'newSession.yolo': 'YOLO 模式',
'newSession.yolo.title': '跳过审批和沙箱',
'newSession.yolo.desc': '启动时使用危险的代理标志。',