fix: detect codex working with static titles (#1574)
* fix: detect codex working with static titles refs #1563 * fix: ignore interrupted codex footer refs #1563 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
This commit is contained in:
parent
f11be93e04
commit
74db2f3a79
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
### Fixed
|
||||
- `herdr config check` now reports unknown config keys with their full paths instead of treating ignored typos as valid configuration. (#1573)
|
||||
- Codex panes with customized static terminal titles now fall back to the live working footer instead of remaining idle, while OSC activity remains preferred. (#1563)
|
||||
- Named agent prompts now honor live bracketed-paste mode before sending Enter, preserving OpenCode text such as `A != B` instead of triggering shell mode. (#1525)
|
||||
- New panes, tabs, layouts, and workspaces using `new_cwd = "follow"` now inherit the foreground process-group leader's working directory instead of an unrelated helper process directory. (#1472)
|
||||
- Noninteractive update, plugin, integration, sound, custom-command, and Git subprocesses no longer flash console windows on Windows. (#1468)
|
||||
|
|
|
|||
|
|
@ -747,6 +747,126 @@ fn codex_background_terminal_screen_does_not_override_osc_idle() {
|
|||
assert!(result.visible_idle);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_screen_working_fallback_handles_static_osc_title() {
|
||||
let screen = "• I’ll run it and wait for completion.\n\n\
|
||||
◦ Working (1m 16s • esc to interrupt) · 1 background…\n\n\
|
||||
› Use /skills to list available skills\n\n\
|
||||
gpt-5.6-sol default · /work\n";
|
||||
let result = osc_explain(Agent::Codex, screen, "project", "");
|
||||
|
||||
assert_eq!(result.state, AgentState::Working);
|
||||
assert_eq!(
|
||||
result.matched_rule.as_ref().map(|r| r.id.as_str()),
|
||||
Some("screen_working_fallback")
|
||||
);
|
||||
assert!(result.visible_working);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_osc_working_remains_preferred_over_screen_fallback() {
|
||||
let screen = "• Working (4s • esc to interrupt)\n\n\
|
||||
› Use /skills to list available skills\n\n\
|
||||
gpt-5.6-sol default · /work\n";
|
||||
let result = osc_explain(Agent::Codex, screen, "⠸ project", "");
|
||||
|
||||
assert_eq!(result.state, AgentState::Working);
|
||||
assert_eq!(
|
||||
result.matched_rule.as_ref().map(|r| r.id.as_str()),
|
||||
Some("osc_title_working")
|
||||
);
|
||||
assert!(result.visible_working);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_screen_blocker_outranks_working_fallback() {
|
||||
let screen = "• Working (4s • esc to interrupt)\n\
|
||||
› 1. Yes, proceed\n\
|
||||
Press enter to confirm or esc to cancel\n";
|
||||
let result = osc_explain(Agent::Codex, screen, "project", "");
|
||||
|
||||
assert_eq!(result.state, AgentState::Blocked);
|
||||
assert_eq!(
|
||||
result.matched_rule.as_ref().map(|r| r.id.as_str()),
|
||||
Some("live_strong_blocker")
|
||||
);
|
||||
assert!(result.visible_blocker);
|
||||
assert!(!result.visible_working);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_weak_blocker_outranks_working_fallback() {
|
||||
let screen = "• Working (4s • esc to interrupt)\n\
|
||||
do you want to continue? [y/n]\n\
|
||||
› Use /skills to list available skills\n";
|
||||
let result = osc_explain(Agent::Codex, screen, "project", "");
|
||||
|
||||
assert_eq!(result.state, AgentState::Blocked);
|
||||
assert_eq!(
|
||||
result.matched_rule.as_ref().map(|r| r.id.as_str()),
|
||||
Some("weak_blocker")
|
||||
);
|
||||
assert!(!result.visible_working);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_transcript_viewer_outranks_working_fallback() {
|
||||
let screen = "• Working (4s • esc to interrupt)\n\
|
||||
› transcript\n\
|
||||
↑/↓ to scroll · pgup/pgdn to move · home/end to jump · q to quit · esc to edit prev\n";
|
||||
let result = osc_explain(Agent::Codex, screen, "project", "");
|
||||
|
||||
assert_eq!(result.state, AgentState::Unknown);
|
||||
assert_eq!(
|
||||
result.matched_rule.as_ref().map(|r| r.id.as_str()),
|
||||
Some("transcript_viewer")
|
||||
);
|
||||
assert!(result.skip_state_update);
|
||||
assert!(!result.visible_working);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_screen_working_fallback_ignores_stale_and_prompt_text() {
|
||||
let screens = [
|
||||
"◦ Working (1m 16s • esc to interrupt)\n\
|
||||
■ Conversation interrupted\n\
|
||||
› Use /skills to list available skills\n\
|
||||
gpt-5.6-sol default · /work\n",
|
||||
"› Explain the text ◦ Working (1m 16s • esc to interrupt)\n\
|
||||
gpt-5.6-sol default · /work\n",
|
||||
" ◦ Working (1m 16s • esc to interrupt)\n\
|
||||
› Use /skills to list available skills\n\
|
||||
gpt-5.6-sol default · /work\n",
|
||||
];
|
||||
|
||||
for screen in screens {
|
||||
let result = osc_explain(Agent::Codex, screen, "project", "");
|
||||
assert_eq!(result.state, AgentState::Idle);
|
||||
assert_eq!(
|
||||
result.matched_rule.as_ref().map(|r| r.id.as_str()),
|
||||
Some("osc_title_idle")
|
||||
);
|
||||
assert!(result.visible_idle);
|
||||
assert!(!result.visible_working);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_screen_working_fallback_ignores_interrupted_short_terminal() {
|
||||
let screen = "◦ Working (1m 16s • esc to interrupt)\n\
|
||||
■ Conversation interrupted\n\
|
||||
›\n";
|
||||
let result = osc_explain(Agent::Codex, screen, "project", "");
|
||||
|
||||
assert_eq!(result.state, AgentState::Idle);
|
||||
assert_eq!(
|
||||
result.matched_rule.as_ref().map(|r| r.id.as_str()),
|
||||
Some("osc_title_idle")
|
||||
);
|
||||
assert!(result.visible_idle);
|
||||
assert!(!result.visible_working);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_osc_working_beats_weak_blocker_screen() {
|
||||
// A stale [y/n] on screen triggers weak_blocker at priority 600, but an
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
id = "codex"
|
||||
version = "2026.07.13.1"
|
||||
version = "2026.07.18.1"
|
||||
min_engine_version = 2
|
||||
updated_at = "2026-07-13T00:00:00Z"
|
||||
updated_at = "2026-07-18T00:00:00Z"
|
||||
|
||||
[[rules]]
|
||||
id = "osc_title_blocked"
|
||||
|
|
@ -56,6 +56,15 @@ any = [
|
|||
{ contains = ["would you like to"], any = [{ contains = ["yes"] }, { contains = ["❯"] }] },
|
||||
]
|
||||
|
||||
[[rules]]
|
||||
id = "screen_working_fallback"
|
||||
state = "working"
|
||||
priority = 500
|
||||
region = "bottom_non_empty_lines(3)"
|
||||
visible_working = true
|
||||
line_regex = ['^[•◦]\s+Working \([^)]*esc to interrupt\)(?: · .*)?$']
|
||||
not = [{ contains = ["■ Conversation interrupted"] }]
|
||||
|
||||
[[rules]]
|
||||
id = "osc_title_idle"
|
||||
state = "idle"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
id = "codex"
|
||||
version = "2026.07.13.1"
|
||||
version = "2026.07.18.1"
|
||||
min_engine_version = 2
|
||||
updated_at = "2026-07-13T00:00:00Z"
|
||||
updated_at = "2026-07-18T00:00:00Z"
|
||||
|
||||
[[rules]]
|
||||
id = "osc_title_blocked"
|
||||
|
|
@ -56,6 +56,15 @@ any = [
|
|||
{ contains = ["would you like to"], any = [{ contains = ["yes"] }, { contains = ["❯"] }] },
|
||||
]
|
||||
|
||||
[[rules]]
|
||||
id = "screen_working_fallback"
|
||||
state = "working"
|
||||
priority = 500
|
||||
region = "bottom_non_empty_lines(3)"
|
||||
visible_working = true
|
||||
line_regex = ['^[•◦]\s+Working \([^)]*esc to interrupt\)(?: · .*)?$']
|
||||
not = [{ contains = ["■ Conversation interrupted"] }]
|
||||
|
||||
[[rules]]
|
||||
id = "osc_title_idle"
|
||||
state = "idle"
|
||||
|
|
|
|||
Loading…
Reference in New Issue