mirror of
https://github.com/herdrdev/herdr.git
synced 2026-09-22 08:01:06 +00:00
* fix: detect codex status with custom interrupt keys * test: remove unverified codex stale-status cases * fix(detect): match punctuation in codex interrupt hints Accept parentheses in remapped interrupt binding labels and simplify the cheap prefilter to a single any-gate shared by both manifest copies. --------- Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
106 lines
3.4 KiB
TOML
106 lines
3.4 KiB
TOML
id = "codex"
|
||
version = "2026.09.15.1"
|
||
min_engine_version = 3
|
||
updated_at = "2026-09-15T00:00:00Z"
|
||
|
||
[[rules]]
|
||
id = "osc_title_blocked"
|
||
state = "blocked"
|
||
priority = 1100
|
||
region = "osc_title"
|
||
visible_blocker = true
|
||
contains = ["Action Required"]
|
||
|
||
[[rules]]
|
||
id = "osc_title_working"
|
||
state = "working"
|
||
priority = 1050
|
||
region = "osc_title"
|
||
visible_working = true
|
||
regex = ['(?:^| )[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏](?: |$)']
|
||
|
||
[[rules]]
|
||
id = "transcript_viewer"
|
||
state = "unknown"
|
||
priority = 1000
|
||
region = "after_last_prompt_marker"
|
||
skip_state_update = true
|
||
contains = ["↑/↓ to scroll", "pgup/pgdn to", "home/end to jump", "q to quit"]
|
||
any = [
|
||
{ contains = ["esc to edit prev"] },
|
||
{ contains = ["esc/← to edit prev"] },
|
||
]
|
||
|
||
[[rules]]
|
||
id = "trust_directory"
|
||
state = "blocked"
|
||
priority = 950
|
||
region = "top_non_empty_lines(20)"
|
||
visible_blocker = true
|
||
all = [
|
||
{ regex = ['\A> You are in [^\r\n]+(?:\r?\n|$)'] },
|
||
{ regex = ['(?s)Do\s+you\s+trust\s+the\s+contents\s+of\s+this\s+directory\?'] },
|
||
]
|
||
|
||
[[rules]]
|
||
id = "startup_update"
|
||
state = "blocked"
|
||
priority = 950
|
||
region = "bottom_non_empty_lines(20)"
|
||
visible_blocker = true
|
||
contains = ["Update available!", "Update now"]
|
||
regex = ['Skip\s+until\s+next\s+version', 'Press enter to continue\s*\z']
|
||
|
||
[[rules]]
|
||
id = "live_strong_blocker"
|
||
state = "blocked"
|
||
priority = 900
|
||
region = "after_last_prompt_marker"
|
||
visible_blocker = true
|
||
any = [
|
||
{ contains = ["press enter to confirm or esc to cancel"] },
|
||
{ contains = ["enter to submit answer"] },
|
||
{ contains = ["enter to submit all"] },
|
||
{ contains = ["allow command?"] },
|
||
]
|
||
|
||
[[rules]]
|
||
id = "weak_blocker"
|
||
state = "blocked"
|
||
priority = 600
|
||
region = "whole_recent_without_current_prompt_marker"
|
||
# Sparkles can replace the space after ›. A later response marker makes that prompt stale.
|
||
not = [{ regex = ['(?m)^›[⠁⠂⠄⠈⠐⠠⡀⢀][^\n]*(?:\n(?:[^•■✗✓\n][^\n]*)?)*\z'] }]
|
||
any = [
|
||
{ contains = ["[y/n]"] },
|
||
{ contains = ["yes (y)"] },
|
||
{ contains = ["do you want to"], any = [{ contains = ["yes"] }, { contains = ["❯"] }] },
|
||
{ contains = ["would you like to"], any = [{ contains = ["yes"] }, { contains = ["❯"] }] },
|
||
]
|
||
|
||
[[rules]]
|
||
id = "screen_working_fallback"
|
||
state = "working"
|
||
priority = 500
|
||
region = "before_current_prompt_marker"
|
||
visible_working = true
|
||
# Support animated and reduced-motion status, including dynamic activity labels.
|
||
# The interrupt hint can be remapped, unbound, or hidden, and queued inputs can
|
||
# sit below the status. Require the live timer suffix with no later response.
|
||
any = [{ contains = [" to interrupt)"] }, { contains = ["s)"] }]
|
||
regex = ['(?m)^(?:[•◦][ \t]+)?[^\s›•◦■✗✓─][^\r\n]* \((?:[0-9]+[hm] )*[0-9]+s(?: • [^\r\n]+? to interrupt)?\)(?: · [^\r\n]*)?(?:\r?\n(?:[^•◦›■✗✓─\r\n][^\r\n]*|•[ \t]+(?:Queued\s+follow-up\s+inputs|Messages\s+to\s+be\s+submitted\s+after\s+next\s+tool\s+call(?:\s+\(press\s+[^\r\n]+?\s+to\s+interrupt\s+and\s+send\s+immediately\))?|Messages\s+to\s+be\s+submitted\s+at\s+end\s+of\s+turn)|›[⠁⠂⠄⠈⠐⠠⡀⢀][^\r\n]*)?)*\s*\z']
|
||
# A failed reconnect keeps its final elapsed timer but is no longer working.
|
||
not = [{ line_regex = ['^(?:[•◦][ \t]+)?Reconnect failed — check the endpoint, then relaunch \([0-9hms ]+\)$'] }]
|
||
|
||
[[rules]]
|
||
id = "osc_title_idle"
|
||
state = "idle"
|
||
priority = 100
|
||
region = "osc_title"
|
||
visible_idle = true
|
||
regex = ['\S']
|
||
not = [
|
||
{ regex = ['(?:^| )[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏](?: |$)'] },
|
||
{ contains = ["Action Required"] },
|
||
]
|