Problem
Health state is determined by pane content diff (health.rs:check()): changed → Running, unchanged → Idle. This creates a blind spot: when an agent is blocked waiting for user input (e.g., Claude Code permission prompt, clarification question, tool approval), the pane is static, so health reports Idle — same as "finished" or "genuinely stuck."
The PM prompt (line 62) acknowledges this ambiguity: "idle" = may have finished or need input, but gives PMs no way to distinguish the cases programmatically. A PM checking GET /api/agents/worker-name sees "health": "idle" and has to guess whether to wait, send input, or kill the worker.
Concrete scenarios
- Claude Code permission prompt — worker hits
Do you want to allow Edit? → pane stops updating → health = idle → PM thinks worker may be done → PM kills or ignores a worker that just needs a y
- Agent asks clarification — worker outputs
Which database driver should I use? → pane static → idle → PM doesn't realize input is needed
- Agent finished — worker outputs
Done. → pane static → idle → same signal as cases 1 & 2
Potential fixes
A. Pattern-based input detection
Scan last_output (or last N lines) for known "waiting for input" patterns and add a WaitingForInput health state:
- Claude Code:
Allow, Do you want to, (Y/n), ? (y/N)
- Generic: lines ending in
?, > , :
Pros: no architectural changes, works with existing pane capture.
Cons: fragile, pattern list needs maintenance per backend, false positives.
B. Structured agent status via sideband file
Each agent writes its status to a known file (e.g., ~/.omar/status/<session>.json) with fields like {"state": "waiting_for_input", "prompt": "Allow Edit?"}. Health checker reads this alongside pane diff.
Pros: precise, no guessing.
Cons: requires agent cooperation (custom tooling or wrapper scripts), doesn't work with unmodified Claude Code/Opencode.
C. Idle duration tiers (use existing config thresholds)
idle_warning (15s) and idle_critical (300s) are already in config but unused. Track time-since-last-change and expose graduated states: Running → Idle → Stuck. Short idle likely means "waiting for input"; long idle likely means "finished" or "crashed."
Pros: uses existing config, simple to implement, no pattern matching.
Cons: still heuristic — can't distinguish "waiting for input" from "thinking."
D. Combine A + C
Use idle duration tiers AND pattern detection. Short idle + input-like last line → WaitingForInput. Long idle + no input pattern → Stuck. This gives PMs the most actionable signal.
Recommendation: Start with C (idle duration tiers) since the config fields already exist, then layer on A (pattern detection) for higher fidelity. Together they cover most real-world cases without requiring agent-side changes.
Problem
Health state is determined by pane content diff (
health.rs:check()): changed →Running, unchanged →Idle. This creates a blind spot: when an agent is blocked waiting for user input (e.g., Claude Code permission prompt, clarification question, tool approval), the pane is static, so health reportsIdle— same as "finished" or "genuinely stuck."The PM prompt (line 62) acknowledges this ambiguity:
"idle" = may have finished or need input, but gives PMs no way to distinguish the cases programmatically. A PM checkingGET /api/agents/worker-namesees"health": "idle"and has to guess whether to wait, send input, or kill the worker.Concrete scenarios
Do you want to allow Edit?→ pane stops updating → health =idle→ PM thinks worker may be done → PM kills or ignores a worker that just needs ayWhich database driver should I use?→ pane static →idle→ PM doesn't realize input is neededDone.→ pane static →idle→ same signal as cases 1 & 2Potential fixes
A. Pattern-based input detection
Scan
last_output(or last N lines) for known "waiting for input" patterns and add aWaitingForInputhealth state:Allow,Do you want to,(Y/n),? (y/N)?,>,:Pros: no architectural changes, works with existing pane capture.
Cons: fragile, pattern list needs maintenance per backend, false positives.
B. Structured agent status via sideband file
Each agent writes its status to a known file (e.g.,
~/.omar/status/<session>.json) with fields like{"state": "waiting_for_input", "prompt": "Allow Edit?"}. Health checker reads this alongside pane diff.Pros: precise, no guessing.
Cons: requires agent cooperation (custom tooling or wrapper scripts), doesn't work with unmodified Claude Code/Opencode.
C. Idle duration tiers (use existing config thresholds)
idle_warning(15s) andidle_critical(300s) are already in config but unused. Track time-since-last-change and expose graduated states:Running→Idle→Stuck. Short idle likely means "waiting for input"; long idle likely means "finished" or "crashed."Pros: uses existing config, simple to implement, no pattern matching.
Cons: still heuristic — can't distinguish "waiting for input" from "thinking."
D. Combine A + C
Use idle duration tiers AND pattern detection. Short idle + input-like last line →
WaitingForInput. Long idle + no input pattern →Stuck. This gives PMs the most actionable signal.Recommendation: Start with C (idle duration tiers) since the config fields already exist, then layer on A (pattern detection) for higher fidelity. Together they cover most real-world cases without requiring agent-side changes.