feat(issue-30): a stall watcher that only notifies
lib_py/stall_watch.py runs beside the wrapper's subscriber for a local herdr job (started after delivery, stopped when the subscriber ends; MAM_STALL_WATCH=0 turns it off). It follows `herdr agent wait` transitions and publishes only `progress` events through one publisher; it has no path that closes a job. - blocked: `stalled:blocked <detection line>` once per episode, never answered. - idle for MAM_STALL_IDLE_S (600) with no background descendants of the agent pid (pane process-info + /proc; adapter resident_children and the heartbeat timer excluded) and an empty input box: `stalled:idle` and one nudge. - input box judged from adapter facts measured on herdr 0.9.1 (codex '›', grok frame '│', opencode '┃' block + stop pattern); the whole-pane fallback is gone. No facts: withheld + `stall_watch:input_check_unavailable` once; never empty for 3 x S: `stall_watch:input_never_empty` once. - herdr failures are not "no event": warning + `stall_watch:herdr_unavailable: <code>` (or agent_not_found), backoff, rc 2 after MAM_STALL_HERDR_FAIL_S (120); the wrapper prints the watcher's last lines. - remote and human jobs are skipped with one log line. - agent_status_reliable is an adapter field; false turns idle judgement off. - the loop runner relays stalled:/stall_watch: notices once each. - scoped_env drops a pane-inherited workspace scope for name lookups (#34 rule). The module sits at the top of lib_py: delegation and loop may not import the agent adapters (their sibling-import rules). Idle pane captures of the five CLIs are fixtures; the mock herdr answers process-info in the real shape. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ec9586bb9b
commit
622c1f8a9d
18 files changed
+1245
-30
No files matched your search
@@ -42,10 +42,20 @@ class CodexAgentAdapter(BaseAgentAdapter):
|
||||
def identity_cache_fields(self) -> tuple:
|
||||
return ('session_id',)
|
||||
|
||||
@property
|
||||
def input_prompt(self) -> Optional[str]:
|
||||
# herdr 0.9.1 capture: one `› Ask Codex to do anything` line, no rules.
|
||||
return '›'
|
||||
|
||||
@property
|
||||
def input_placeholder(self) -> Optional[str]:
|
||||
return 'Ask Codex to do anything'
|
||||
|
||||
@property
|
||||
def resident_children(self) -> tuple:
|
||||
# Kept alive while idle (measured 2026-09-25).
|
||||
return ('codex-code-mode-host',)
|
||||
|
||||
def _codex_home(self, ctx: DiscoveryContext) -> str:
|
||||
return os.environ.get('CODEX_HOME') or f"{ctx.home_dir}/.codex"
|
||||
|
||||
|
||||
@@ -49,6 +49,11 @@ class GrokAgentAdapter(BaseAgentAdapter):
|
||||
def input_rule_pattern(self) -> str:
|
||||
return '─{10,}'
|
||||
|
||||
@property
|
||||
def input_frame_chars(self) -> str:
|
||||
# The input box is drawn as ╭─╮ / │ ❯ │ / ╰─╯ (herdr 0.9.1 capture).
|
||||
return '│'
|
||||
|
||||
def _ws_dir(self, ctx: DiscoveryContext) -> str:
|
||||
return quote(os.path.realpath(ctx.cwd), safe='')
|
||||
|
||||
|
||||
@@ -45,8 +45,20 @@ class OpenCodeAgentAdapter(BaseAgentAdapter):
|
||||
|
||||
@property
|
||||
def input_rule_pattern(self) -> Optional[str]:
|
||||
# Kept for lib.sh's readiness corroboration; the input box itself is a
|
||||
# ┃ block (input_block_prefix), which the extractor reads first.
|
||||
return '─{10,}'
|
||||
|
||||
@property
|
||||
def input_block_prefix(self) -> Optional[str]:
|
||||
return '┃'
|
||||
|
||||
@property
|
||||
def input_stop_pattern(self) -> Optional[str]:
|
||||
# The input area ends at the right-aligned cwd line (a long run of
|
||||
# blanks after the ┃) or the `Build auto · <model>` status line.
|
||||
return r'^\s{20,}\S|^\s*(Build|Plan)\b.*·'
|
||||
|
||||
def _resolve_db(self, ctx: DiscoveryContext) -> Optional[str]:
|
||||
# 1. Try opencode CLI "opencode db path"
|
||||
try:
|
||||
|
||||
Reference in new issue
Block a user