Agent Detection
How MadoHub identifies which AI agent is running and tracks its state.
MadoHub monitors terminal output in real-time to detect which AI agent is running and what state it's in. This powers the visual indicators on tile title bars and triggers routing connections.
Supported agents
Claude Code
Detected by Unicode markers in terminal output:
| Marker | Meaning |
|---|---|
| ❯ (U+2770) | Claude Code prompt |
| ✽ (U+273D) | Thinking/spinner |
| ⏺ (U+23FA) | Tool use indicator |
Also matches the literal string "Claude Code" in output.
OpenAI Codex CLI
Detected by these patterns:
OpenAI Codex— Branding linecodex>— TUI promptopenai/codex— Package referencecodex v[0-9]— Version string> _ OpenAI Codex— Startup bannercodex --dangerously— Sandbox-bypass flag (case-insensitive)codex...fast— Fast-model invocation (case-insensitive)gpt-[0-9.]+ x?high— Model info
Amp and OpenCode
Amp and OpenCode are also recognized as agent kinds, but not by the frontend terminal-text sniffer above — it only classifies Claude Code, Codex, and generic shell. Amp and OpenCode sessions are identified separately, by a backend process-tree scan that walks the terminal's descendant processes looking for their executables.
Generic shell
If no agent markers are detected, the terminal is treated as a standard shell session.
Agent states
MadoHub tracks seven states for each terminal:
| State | Detection | Visual |
|---|---|---|
| Working | "esc to interrupt" in status bar, or ✽/⏺ with ellipsis | Green pulsing border |
| Idle | "? for shortcuts" in status bar | No indicator |
| Waiting | "Allow once" / "Allow always" / "Deny" / "Do you want to proceed?" / "(y/n)" / "requires approval" dialogs | Orange border |
| Stopped | Completion keyword detected via terminal scraping (ambiguous — task may have finished successfully or not) | Red indicator |
| Completed | Confirmed successful finish, parsed from the agent's JSONL/DB session log (e.g. Codex task_complete) | Green indicator |
| Failed | Confirmed failure, parsed from the agent's JSONL/DB session log (e.g. Codex turn_aborted / error) | Red indicator |
| Exited | The underlying PTY process is gone (terminal state) | Gray/dim indicator |
Each state carries a stateSource tag of either pty-scrape or jsonl-watcher, so the UI (and any routing logic) can tell a live terminal-scraping guess apart from a verdict confirmed by reading the agent's own session transcript. "Stopped" is always pty-scrape and inherently ambiguous; "Completed" and "Failed" come from jsonl-watcher and are treated as authoritative.
Completion detection
MadoHub detects when an agent finishes a task by matching a shape, not a fixed word list: a line-start spinner glyph, followed by a capitalized past-tense verb, followed by " for " and a duration.
The regex pattern:
/(?:^\s*[✶✽✳✢·∗*]\s*([A-Z][a-zé]+(?:ed|t))|\b(Saut[ée]*d|Cogitated))\s+for\s+(?:\d+\s*[hm]\s*)*\d+\s*[hms]\b/Claude Code rotates the verb across releases (Worked, Churned, Sautéed, Cogitated, and others), so the verb itself is deliberately not enumerable — matching relies on the shape instead. A realistic match looks like:
✽ Worked for 2m 30sThe line-start glyph anchor (one of ✶✽✳✢·∗*) is what prevents false positives from ordinary prose such as "Retried for 3s". Sautéed and Cogitated survive in the pattern only as a glyph-less legacy fallback for older CLI output that predates the glyph-anchored format.
When a completion is detected, the agent state changes to "stopped" and any connected routing triggers may fire.