From 87571bb694371770b4bc3fc92d45b58b9849a674 Mon Sep 17 00:00:00 2001 From: sean2077 <37324769+sean2077@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:01:44 +0800 Subject: [PATCH 1/2] feat(codex): support max reasoning effort --- agent/codex/codex.go | 4 +++- agent/codex/session_test.go | 11 ++++++++++- core/i18n.go | 10 +++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/agent/codex/codex.go b/agent/codex/codex.go index 1312edb5f9..5ffaf065cc 100644 --- a/agent/codex/codex.go +++ b/agent/codex/codex.go @@ -153,6 +153,8 @@ func normalizeReasoningEffort(raw string) string { return "high" case "xhigh", "x-high", "very-high": return "xhigh" + case "max", "maximum": + return "max" default: return "" } @@ -200,7 +202,7 @@ func (a *Agent) GetReasoningEffort() string { } func (a *Agent) AvailableReasoningEfforts() []string { - return []string{"low", "medium", "high", "xhigh"} + return []string{"low", "medium", "high", "xhigh", "max"} } func (a *Agent) configuredModels() []core.ModelOption { diff --git a/agent/codex/session_test.go b/agent/codex/session_test.go index c789557e26..20aef6d7ad 100644 --- a/agent/codex/session_test.go +++ b/agent/codex/session_test.go @@ -23,10 +23,19 @@ func TestNormalizeReasoningEffort_RejectsMinimal(t *testing.T) { } } +func TestNormalizeReasoningEffort_AcceptsMax(t *testing.T) { + if got := normalizeReasoningEffort("max"); got != "max" { + t.Fatalf("normalizeReasoningEffort(max) = %q, want max", got) + } + if got := normalizeReasoningEffort("maximum"); got != "max" { + t.Fatalf("normalizeReasoningEffort(maximum) = %q, want max", got) + } +} + func TestAvailableReasoningEfforts_ExcludesMinimal(t *testing.T) { agent := &Agent{} got := agent.AvailableReasoningEfforts() - want := []string{"low", "medium", "high", "xhigh"} + want := []string{"low", "medium", "high", "xhigh", "max"} if len(got) != len(want) { t.Fatalf("AvailableReasoningEfforts len = %d, want %d, got=%v", len(got), len(want), got) } diff --git a/core/i18n.go b/core/i18n.go index 6822451539..04fac2ccb5 100644 --- a/core/i18n.go +++ b/core/i18n.go @@ -2483,11 +2483,11 @@ var messages = map[MsgKey]map[Language]string{ LangSpanish: "Niveles de razonamiento disponibles:\n", }, MsgReasoningUsage: { - LangEnglish: "Usage: `/reasoning ` or `/reasoning `", - LangChinese: "用法: `/reasoning <序号>` 或 `/reasoning `", - LangTraditionalChinese: "用法: `/reasoning <序號>` 或 `/reasoning `", - LangJapanese: "使い方: `/reasoning <番号>` または `/reasoning `", - LangSpanish: "Uso: `/reasoning ` o `/reasoning `", + LangEnglish: "Usage: `/reasoning ` or `/reasoning `", + LangChinese: "用法: `/reasoning <序号>` 或 `/reasoning `", + LangTraditionalChinese: "用法: `/reasoning <序號>` 或 `/reasoning `", + LangJapanese: "使い方: `/reasoning <番号>` または `/reasoning `", + LangSpanish: "Uso: `/reasoning ` o `/reasoning `", }, MsgModeUsage: { LangEnglish: "\nUse `/mode ` to switch.\nAvailable: %s", From 698aec0550e9c3df69f1345ff22b8983793609b1 Mon Sep 17 00:00:00 2001 From: sean2077 <37324769+sean2077@users.noreply.github.com> Date: Sat, 15 Aug 2026 22:55:57 +0800 Subject: [PATCH 2/2] fix(core): list agent-specific reasoning levels --- CHANGELOG.md | 1 + agent/codex/codex.go | 46 +++++++++++++++++++++++++++++--------------- core/engine.go | 10 +++++++--- core/engine_test.go | 20 +++++++++++++++++++ core/i18n.go | 10 +++++----- 5 files changed, 64 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index febe332728..b297794b89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased ### Added +- **codex**: support `max` reasoning effort in project configuration and through `/reasoning max` (#1683). - **`agent_session_idle_timeout_mins`**: new per-project config option that closes an idle live agent process after a clean turn while preserving the cc-connect session and saved agent session ID. The next message starts a new agent process and resumes the same conversation. Set to `0` or leave unset to disable (#1338). - **Reasonix agent**: new agent adapter for Reasonix multi-model coding agent, bridging via HTTP serve API (POST /submit, SSE /events, POST /approve). Supports default/yolo/plan permission modes, SSE auto-reconnect with backoff, and thinking accumulator. (#1281) - **cloud_web platform**: 新增 self-hosted IM Gateway 作为 first-class platform 接入 (CWIP v1 协议,支持 websocket / long_poll / gateway 3 种 transport,完整 inbound/outbound + capability negotiation + graceful degradation)。 详见 docs/cloud-web.md + #1282。 diff --git a/agent/codex/codex.go b/agent/codex/codex.go index 5ffaf065cc..5ab193a295 100644 --- a/agent/codex/codex.go +++ b/agent/codex/codex.go @@ -51,6 +51,19 @@ type Agent struct { mu sync.RWMutex } +type reasoningEffortDefinition struct { + name string + aliases []string +} + +var reasoningEffortDefinitions = []reasoningEffortDefinition{ + {name: "low"}, + {name: "medium", aliases: []string{"med"}}, + {name: "high"}, + {name: "xhigh", aliases: []string{"x-high", "very-high"}}, + {name: "max", aliases: []string{"maximum"}}, +} + func New(opts map[string]any) (core.Agent, error) { workDir, _ := opts["work_dir"].(string) if workDir == "" { @@ -142,22 +155,21 @@ func normalizeMode(raw string) string { } func normalizeReasoningEffort(raw string) string { - switch strings.ToLower(strings.TrimSpace(raw)) { - case "": - return "" - case "low": - return "low" - case "medium", "med": - return "medium" - case "high": - return "high" - case "xhigh", "x-high", "very-high": - return "xhigh" - case "max", "maximum": - return "max" - default: + normalized := strings.ToLower(strings.TrimSpace(raw)) + if normalized == "" { return "" } + for _, definition := range reasoningEffortDefinitions { + if normalized == definition.name { + return definition.name + } + for _, alias := range definition.aliases { + if normalized == alias { + return definition.name + } + } + } + return "" } func (a *Agent) Name() string { return "codex" } @@ -202,7 +214,11 @@ func (a *Agent) GetReasoningEffort() string { } func (a *Agent) AvailableReasoningEfforts() []string { - return []string{"low", "medium", "high", "xhigh", "max"} + efforts := make([]string, len(reasoningEffortDefinitions)) + for i, definition := range reasoningEffortDefinitions { + efforts[i] = definition.name + } + return efforts } func (a *Agent) configuredModels() []core.ModelOption { diff --git a/core/engine.go b/core/engine.go index 9b8c5e74f6..0c4da013ad 100644 --- a/core/engine.go +++ b/core/engine.go @@ -9768,7 +9768,7 @@ func (e *Engine) cmdReasoning(p Platform, msg *Message, args []string) { buttons = append(buttons, row) } sb.WriteString("\n") - sb.WriteString(e.i18n.T(MsgReasoningUsage)) + sb.WriteString(e.reasoningUsage(efforts)) e.replyWithButtons(p, msg.ReplyCtx, sb.String(), buttons) return } @@ -9790,7 +9790,7 @@ func (e *Engine) cmdReasoning(p Platform, msg *Message, args []string) { } } if !valid { - e.reply(p, msg.ReplyCtx, e.i18n.T(MsgReasoningUsage)) + e.reply(p, msg.ReplyCtx, e.reasoningUsage(efforts)) return } @@ -9805,6 +9805,10 @@ func (e *Engine) cmdReasoning(p Platform, msg *Message, args []string) { e.reply(p, msg.ReplyCtx, e.i18n.Tf(MsgReasoningChanged, target)) } +func (e *Engine) reasoningUsage(efforts []string) string { + return e.i18n.Tf(MsgReasoningUsage, strings.Join(efforts, "|")) +} + func (e *Engine) cmdMode(p Platform, msg *Message, args []string) { agent, _, _, err := e.commandContext(p, msg) if err != nil { @@ -12917,7 +12921,7 @@ func (e *Engine) renderReasoningCard() *Card { Markdown(sb.String()). Select(e.i18n.T(MsgReasoningSelectPlaceholder), opts, initVal). Buttons(e.cardBackButton()) - cb.Note(e.i18n.T(MsgReasoningUsage)) + cb.Note(e.reasoningUsage(efforts)) return cb.Build() } diff --git a/core/engine_test.go b/core/engine_test.go index b68cf7f6b6..956244c529 100644 --- a/core/engine_test.go +++ b/core/engine_test.go @@ -445,6 +445,7 @@ type stubModelModeAgent struct { model string mode string reasoningEffort string + reasoningLevels []string providers []ProviderConfig active string } @@ -545,6 +546,9 @@ func (a *stubModelModeAgent) GetReasoningEffort() string { } func (a *stubModelModeAgent) AvailableReasoningEfforts() []string { + if a.reasoningLevels != nil { + return a.reasoningLevels + } return []string{"low", "medium", "high", "xhigh"} } @@ -5506,6 +5510,22 @@ func TestCmdReasoning_RejectsMinimal(t *testing.T) { } } +func TestCmdReasoning_UsageListsOnlyAgentReasoningEfforts(t *testing.T) { + p := &stubPlatformEngine{n: "plain"} + agent := &stubModelModeAgent{reasoningLevels: []string{"off", "minimal", "low", "medium", "high", "xhigh"}} + e := NewEngine("test", agent, []Platform{p}, "", LangEnglish) + msg := &Message{SessionKey: "test:user1", ReplyCtx: "ctx"} + + e.cmdReasoning(p, msg, []string{"unsupported"}) + + if len(p.sent) != 1 || !strings.Contains(p.sent[0], "/reasoning ") { + t.Fatalf("sent = %v, want usage with agent-provided reasoning efforts", p.sent) + } + if strings.Contains(p.sent[0], "max") { + t.Fatalf("sent = %v, usage advertised an unavailable reasoning effort", p.sent) + } +} + // TestCmdReasoning_MultiWorkspaceSavesToWorkspaceSessions is a regression test // for the bug where cmdReasoning called e.sessions.Save() (global) instead of // sessions.Save() (workspace-resolved), leaving workspace session state unsaved. diff --git a/core/i18n.go b/core/i18n.go index 04fac2ccb5..de521bdbb5 100644 --- a/core/i18n.go +++ b/core/i18n.go @@ -2483,11 +2483,11 @@ var messages = map[MsgKey]map[Language]string{ LangSpanish: "Niveles de razonamiento disponibles:\n", }, MsgReasoningUsage: { - LangEnglish: "Usage: `/reasoning ` or `/reasoning `", - LangChinese: "用法: `/reasoning <序号>` 或 `/reasoning `", - LangTraditionalChinese: "用法: `/reasoning <序號>` 或 `/reasoning `", - LangJapanese: "使い方: `/reasoning <番号>` または `/reasoning `", - LangSpanish: "Uso: `/reasoning ` o `/reasoning `", + LangEnglish: "Usage: `/reasoning ` or `/reasoning <%s>`", + LangChinese: "用法: `/reasoning <序号>` 或 `/reasoning <%s>`", + LangTraditionalChinese: "用法: `/reasoning <序號>` 或 `/reasoning <%s>`", + LangJapanese: "使い方: `/reasoning <番号>` または `/reasoning <%s>`", + LangSpanish: "Uso: `/reasoning ` o `/reasoning <%s>`", }, MsgModeUsage: { LangEnglish: "\nUse `/mode ` to switch.\nAvailable: %s",