Hold codex in thinking while a turn is running
Codex uses the osc_title_stability idle strategy, but it draws its
progress in the pane body ('Working … esc to interrupt'), not the OSC
title. The title goes stable mid-turn, so ~2s later the classifier
declared codex idle while it was still working. Add a thinking-promoter
pattern ((?i)esc to interrupt) to the codex built-in preset; classify()
checks promoter regexes against the rendered screen before the
title-stability verdict, so codex stays in thinking until the turn's
in-progress footer actually disappears.
Resolves the [CODEX IDLE] TODO item.
This commit is contained in:
@@ -57,6 +57,21 @@ func TestClassifyTitleStability(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyTitleStabilityThinkingPatternOverridesIdle(t *testing.T) {
|
||||
cfg := &resolvedIdleDetection{
|
||||
strategy: StrategyOSCTitleStability,
|
||||
idleThresholdMS: 2000,
|
||||
thinkingRegexes: []*regexp.Regexp{mustCompile(t, `(?i)esc to interrupt`)},
|
||||
}
|
||||
screen := []byte("• Working (5s • esc to interrupt)")
|
||||
if got, _ := classify(cfg, false, false, 9999, 5000, "codex", nil, screen); got != StateThinking {
|
||||
t.Fatalf("thinking screen marker: got %q want %q", got, StateThinking)
|
||||
}
|
||||
if got, _ := classify(cfg, false, false, 9999, 5000, "codex", nil, []byte(">_")); got != StateIdle {
|
||||
t.Fatalf("stable title without marker: got %q want %q", got, StateIdle)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifyTitleStatus(t *testing.T) {
|
||||
cfg := &resolvedIdleDetection{
|
||||
strategy: StrategyOSCTitleStatus,
|
||||
|
||||
@@ -352,7 +352,10 @@ func defaultAgentPresets() []*Preset {
|
||||
"ready_signal": { "idle_ms": 1000 },
|
||||
"idle_detection": {
|
||||
"strategy": "osc_title_stability",
|
||||
"idle_threshold_ms": 2000
|
||||
"idle_threshold_ms": 2000,
|
||||
"thinking_patterns": [
|
||||
"(?i)esc to interrupt"
|
||||
]
|
||||
},
|
||||
"chrome_trim_hints": [
|
||||
"^OpenAI Codex",
|
||||
|
||||
@@ -27,6 +27,13 @@ func TestLoadUsesBuiltInDefaultsWithoutWritingConfig(t *testing.T) {
|
||||
if claude.IdleDetection == nil || len(claude.IdleDetection.PermissionPatterns) == 0 {
|
||||
t.Fatalf("built-in claude missing permission patterns: %+v", claude.IdleDetection)
|
||||
}
|
||||
codex := presetByName(set.Agents, "codex")
|
||||
if codex == nil {
|
||||
t.Fatal("missing built-in codex preset")
|
||||
}
|
||||
if codex.IdleDetection == nil || len(codex.IdleDetection.ThinkingPatterns) == 0 {
|
||||
t.Fatalf("built-in codex missing thinking patterns: %+v", codex.IdleDetection)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadMergesUserOverlayIntoBuiltInPreset(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user