Detection Reference¶
How agentwatch detects programs and states.
Overview¶
agentwatch uses pattern matching on terminal content to detect:
- Program - Which program is running (Claude Code, Codex, shell)
- State - What the program is doing (Idle, Working, Blocked, Error)
Each detection produces a confidence score (0.0 to 1.0). Detections with confidence > 0.5 are used.
Detection Pipeline¶
- Capture: Raw terminal content from tmux
- Strip: Remove ANSI escape codes for analysis
- Program: Match patterns to identify the program
- State: Use program-specific patterns for state
- Result: Program, state, detail, and confidence scores
Programs¶
Claude Code¶
Detected by:
| Pattern | Confidence | Description |
|---|---|---|
| Version header | 1.0 | Claude Code vX.X.X in header |
| Trust dialog | 0.95 | "Do you trust the files" prompt |
| Prompt with hints | 0.9 | ❯ prompt with "? for shortcuts" |
| Welcome message | 0.85 | "Welcome back" with ❯ prompt |
| Just prompt | 0.6 | ❯ character alone |
Codex¶
Detected by header patterns and UI elements specific to Codex.
Shell (bash/zsh)¶
Detected by shell prompt patterns:
| Pattern | Description |
|---|---|
user@host:path$ |
Standard bash prompt |
$ at line end |
Generic shell prompt |
% at line end |
zsh default prompt |
Shell sessions are always considered Idle.
States¶
Idle¶
The program is waiting for user input.
Detection Patterns:
| Pattern | Weight | Description |
|---|---|---|
| Autosuggest visible | 100 | ❯ text ↵ - strongest idle signal |
| Completion message | 60 | "Brewed for 3m 14s" etc. |
| Welcome screen | 50 | Fresh start, no conversation |
| Shortcuts hint | 45 | "? for shortcuts" visible |
| Prompt at bottom | 40 | ❯ near bottom of screen |
| Little content after prompt | 25 | ≤3 lines after prompt |
| Has prompt | 20 | ❯ visible anywhere |
How it works:
Patterns are weighted and summed. If idle score > working score, state is Idle.
Working¶
The program is actively processing.
Detection Patterns:
| Pattern | Weight | Description |
|---|---|---|
| Compacting conversation | 95 | "Compacting conversation…" |
| Esc to interrupt + thinking | 95 | Active thinking indicator |
| Esc to interrupt | 90 | Status line during processing |
| Running indicator | 90 | "Running…" visible |
| Ctrl+b background hint | 85 | Command actively running |
| Queued messages | 80 | User typed while processing |
| Running in background | 75 | Background task active |
| Activity status line | 65 | ✶ Doing… with timing |
| Tool execution | 55 | Bash ───── tool boxes |
| Spinner animation | 50 | Braille spinners (⠋⠙⠹) |
| Streaming response | 35 | Lots of content after prompt |
Spinner Characters:
These indicate active processing when visible.
Blocked¶
The program needs user action to continue.
Detection Patterns:
| Pattern | Weight | Description |
|---|---|---|
| Trust dialog | 100 | "Do you trust the files" |
| Navigation hint | 95 | "↑/↓ to navigate" |
| Esc to cancel | 95 | Dismissible dialog |
| Proceed prompt | 90 | "Do you want to proceed?" |
| Type to search | 90 | Fuzzy finder active |
| Menu selection | 85 | ❯ 1. Option list |
| Enter to select | 85 | Selection required |
| Allow permission | 80 | "Allow X?" prompt |
| Yes/No confirmation | 30 | Generic y/n prompt |
Detail Values:
| Detail | Meaning |
|---|---|
| Permission dialog | Needs y/n approval |
| Menu selection | Choose from options |
| Trust dialog | Workspace trust prompt |
| Search picker | Fuzzy finder open |
Error¶
Something went wrong.
Detection Patterns:
| Pattern | Weight | Description |
|---|---|---|
| Hung state | 90 | >5 min "working" but prompt visible |
| Error keyword | 70 | Error: at line start |
| Error with message | 65 | Error: <message> |
| API error | 65 | "API request error" |
| ERROR in caps | 60 | All-caps ERROR |
| Rate limit | 60 | Rate limiting messages |
| Crash | 60 | "crash" or "crashed" |
| Failed to | 55 | "failed to X" |
| Timeout | 55 | "timed out" |
| Connection error | 55 | "connection refused" etc. |
| Exception | 50 | "exception" keyword |
Hung Detection:
If the prompt is at the bottom but status shows >5 minutes of working with 0 tokens consumed, this indicates a stuck state (Claude Code bug).
Weight System¶
State detection uses weighted scoring:
- Match all patterns against terminal content
- Sum weights for each state (Idle, Working, Blocked, Error)
- Highest score wins
Example:
Terminal shows:
- "❯" prompt (Idle: +20)
- "? for shortcuts" (Idle: +45)
- Near bottom of screen (Idle: +40)
- No spinner visible
Idle total: 105
Working total: 0
Blocked total: 0
Result: Idle (confidence based on margin)
Confidence Calculation¶
Confidence reflects how certain the detection is:
- 1.0: Definitive match (e.g., version header)
- 0.9+: Very high confidence
- 0.7-0.9: High confidence
- 0.5-0.7: Moderate confidence
- <0.5: Detection rejected
Activity Markers¶
These characters appear before status messages:
Used in patterns like:
✶ Doing…· Thinking…✻ Brewed for 3m 14s
Box Drawing Characters¶
UI chrome characters (ignored in content analysis):
Lines containing only these are skipped when analyzing content.
Debugging Detection¶
Enable Debug Logging¶
service:
debug:
enabled: true
capture_dir: ~/.cache/agentwatch/captures
log_state_changes: true
log_detections: true
State Change Markers¶
State changes are logged with markers for easy grep:
View Captures¶
# Find state change in logs
grep "STATE CHANGE" ~/.cache/agentwatch/daemon.log
# View the capture that triggered it
cat ~/.cache/agentwatch/captures/session-name/capture_*.txt
Known Issues¶
Phantom Working State¶
Claude Code bug: Sometimes shows working indicators (spinner, status line) but is actually idle. Detected by:
- Prompt visible at bottom
- Status shows >5 minutes working
- Zero tokens consumed ("↓ 0 tokens")
agentwatch detects this with high weight (300) to override false working signals.
Stale Status Lines¶
After Claude finishes, status lines may remain visible while the prompt scrolls into view. The weight system handles this by giving stronger weight to idle indicators like the prompt position.
Extending Detection¶
Custom Detectors¶
Detectors extend BaseDetector and implement:
class MyDetector(BaseDetector):
def detect_program(self, text: str) -> DetectionResult:
# Return program name and confidence
pass
def detect_state(self, text: str, program: str) -> DetectionResult:
# Return state and detail
pass
Adding Patterns¶
Patterns are defined in agentwatch/detectors/patterns.py: