Working with Agents
artuditu treats your AI agents as first-class citizens of the terminal: it knows which panes run agents, what state each is in, and how to bring a conversation back after a restart, all without wrapping or modifying the agents themselves.
The AGENTS sidebar
Section titled “The AGENTS sidebar”Every pane running a recognized agent (Claude Code, Copilot, Codex, opencode, Kimi, Grok, Pi, Cursor, Gemini, Qwen, Kiro, Aider, Amp, Droid, or fx) appears in the sidebar with a live state:
| State | Meaning | How it’s detected |
|---|---|---|
| 🔴 blocked | waiting on you: approve/deny is on screen | a permission prompt is visible |
| 🟠 working | the agent is generating | a spinner or “esc to interrupt” hint on screen |
| 🟢 done | finished while unfocused | sustained quiet after working |
| idle | quiet, nothing pending | no recent activity |
A working agent shows a rotating-circle spinner in its status dot, so you can tell at a glance which agents are still busy. When one finishes or blocks, artuditu can play a short retro chime. The sound is optional and off by default, turn it on in Settings → General.
Click a row to jump to that agent’s pane from anywhere, across workspaces.
States are debounced: an agent that pauses mid-turn (thinking, tool calls, API latency) holds at working instead of flickering. You get one clean working → done per turn, not a dozen flaps.
Working needs proof. An agent counts as working only when a real generating indicator is on screen, like a spinner or an interrupt hint. Output alone proves nothing. A CLI printing its whole welcome screen at launch is idle, your keystrokes echoing while you compose a prompt are idle, a scrolling log is idle. That way you never get a false state or a false completion chime.
The All / Active toggle in the header switches between live agents only (default) and the full resumable history.
Usage in Mission Control
Section titled “Usage in Mission Control”Mission Control reads the structured usage counters that each agent already stores for its own session. It shows input, output, and cache tokens, context usage when the agent records its context limit, and cost when the agent records one or Artuditu has pricing for the model.
| Native usage reader | Data source |
|---|---|
| Claude Code | project transcript |
| Codex | rollout token counters |
| GitHub Copilot CLI | session shutdown metrics |
| opencode | read-only session database |
| Kimi | session status records |
| Grok | completed-turn usage records |
| Pi | assistant message usage |
| Gemini and Qwen | project chat records |
| fx | session usage snapshot |
Aider, Kiro, Cursor, Amp, Droid, and manifest-defined agents still receive live
identity and state detection, but show — for usage until they expose a stable
per-session counter or report one through an integration. Artuditu never estimates
tokens from transcript text.
Usage parsing runs off the render path only when Mission Control becomes active, its scope changes, or you choose Refresh. A hidden dashboard schedules no usage work. Unchanged sessions are cached by agent, session ID, and source modification time. JSONL readers cap individual records, so an unusually large tool result cannot cause an equally large temporary allocation.
How artuditu knows which agent a pane is running
Section titled “How artuditu knows which agent a pane is running”An agent is a program, so artuditu asks the operating system rather than guessing
from what a pane happens to be showing. It scans the processes running under
each pane and matches the program name. A pane running claude is Claude Code;
a pane that merely prints the word “claude” is a shell.
That distinction matters more than it sounds. Names like amp, cursor,
droid, grok and pi are ordinary English words, so reading them off the
screen turns “for example” into Amp and “cursor is out of bounds” into Cursor.
Your sidebar fills with agents that were never running.
The scan covers every pane at once, a couple of times a second, off the render path, so it costs nothing you can feel.
Where artuditu cannot see processes, it falls back to reading text, ranked by how deliberate that text is: the command the pane was started with, then the window title the agent sets for itself, then its output. Names that double as ordinary words are only ever believed from the first two, never from output. This fallback covers Windows and remote panes, so detection degrades there, it does not break.
Sound alerts
Section titled “Sound alerts”artuditu can play a short retro chime when an agent finishes a working stretch or blocks on a permission prompt. Both sounds are off by default because sound is a matter of taste. Turn them on in Settings → General, and use the Test sound row to hear the chime before you commit to it. The blocked chime rings at most once until you’ve actually looked at the pane, so a repeating prompt can’t spam you.
Session resume: the flagship
Section titled “Session resume: the flagship”artuditu reopens each agent’s own conversation after a restart, with zero configuration. It discovers the agent’s most recent session from the agent’s native on-disk store, keyed to the pane’s folder, and runs the right resume command for you:
| Agent | Resumes via |
|---|---|
| Claude Code | its project transcript store |
| GitHub Copilot CLI | its session-state store |
| Codex | its rollout files |
| opencode | its session storage |
| Kimi | its session index (~/.kimi-code/session_index.jsonl) |
| Grok | its session directory (~/.grok/sessions) |
| Pi | its session store (~/.pi/agent/sessions) |
| Gemini | its project chat store (~/.gemini/tmp) |
| Qwen | its project chat store (~/.qwen/tmp) |
| fx | its session store (~/.fx/sessions) |
| Cursor | resume command (when the session id is known) |
You’ll also see recent sessions listed at the bottom of the AGENTS sidebar (toggle All): click one to reopen it into a new pane, even sessions from before you installed artuditu. The ✕ hides an entry from the list. It never touches the agent’s stored session.
Fork a session into a new pane
Section titled “Fork a session into a new pane”Sometimes you want to try a different direction without losing where you are. Fork to New Pane branches the agent conversation in the current pane into a brand new pane beside it. The fork inherits the entire context of the original session, but under a new session id, so the two run independently: keep the original going in the left pane and explore the branch on the right, with no crossed wires.
Two ways to trigger it:
- Right-click the pane and choose Fork to New Pane.
- Press
Ctrl+Space fto fork the focused pane. - Run
artu agent fork <target>from the CLI. Add--name <alias>to name the new fork or--no-focusto leave the current view unchanged.
The new pane opens straight into the agent, already caught up on the whole conversation, exactly as if you had resumed it, except the original session is untouched and the two diverge from here.
Forking uses the agent’s own fork command, so it is available for agents that support it natively:
| Agent | Forks via |
|---|---|
| Claude Code | claude --resume <id> --fork-session |
| Grok Build | grok --resume <id> --fork-session |
| Codex | codex fork <id> |
| Pi | pi --fork <id> |
The TUI action only appears for supported agents and otherwise remains a no-op. The CLI reports a structured error instead, so automation never mistakes an unsupported or unresolved fork for success.
Codex forks require the exact session identity reported by its integration or
recorded when Artuditu resumes a session. Artuditu never guesses the newest Codex
rollout in a shared folder, because that could fork another pane’s active
conversation. Install or refresh the hook with
artu integration install codex; its SessionStart and prompt hooks bind new
and resumed Codex panes to their exact rollout.
Precise events: the integration hook
Section titled “Precise events: the integration hook”Screen-based detection needs no setup and works for everything. The optional hook adds precision: the agent itself reports its exact session id and lifecycle events (permission prompt raised, turn ended) into artuditu:
artu integration install claude # or: copilot · codex · opencode · kimior toggle it in Settings → Integrations. What it does per agent:
- claude / copilot / codex: registers a small session-start hook script in the agent’s own settings file.
- opencode: installs a tiny plugin file.
- kimi: adds a
[[hooks]]entry to~/.kimi-code/config.toml, edited in place so your API keys, comments, and own hooks are left untouched.
Uninstalling (artu integration uninstall <agent>, or the same Settings
toggle) surgically removes only artuditu’s hook entry. Your other settings
and the agent installation itself are never touched.
Custom detection rules
Section titled “Custom detection rules”Detection ships with built-in defaults that already cover the known agents. If
you run an agent artuditu does not recognize, or you want to tune how an existing
one reads, add your own TOML files in ~/.artuditu/manifests/. Name each file
after its agent (myagent.toml) to keep things findable. Every *.toml in that
folder merges on top of the built-ins, and the first time artuditu runs it drops an
annotated example.toml.txt there to copy from.
A manifest controls two separate things:
[identity]decides which agent a pane is running[[rule]]decides what state that agent is in
Teaching artuditu a new agent
Section titled “Teaching artuditu a new agent”[identity] lists the names that identify the agent, matched as whole words.
The two lists differ in how far each name is trusted:
| List | Trusted in |
|---|---|
distinct | anywhere, including whatever the pane prints |
ambiguous | only the command that started the pane, or the agent’s own window title |
Put a name in ambiguous when it is also an ordinary word, so a pane that
happens to print it is not mistaken for the agent. Add replace = true to drop
artuditu’s built-in names instead of adding to them, which is how you remove a
default you disagree with.
agent = "cursor"
[identity]distinct = ["cursor-agent"] # the CLI binary: unmistakableambiguous = ["cursor"] # also a normal English wordNaming an agent artuditu does not ship teaches it a new one, no rebuild and no waiting for a release:
agent = "myagent"
[identity]distinct = ["myagent-cli"]State rules
Section titled “State rules”A rule matches text on the pane’s screen (or its window title) and, when it holds, sets the agent’s state. Rules carry a priority, so the highest-priority match wins and a rule of yours can override a built-in one for the same agent.
# Which agent this file applies to. "generic" (the default) means every agent,# and is only valid for rules: identity always needs a specific agent.agent = "myagent"
[[rule]]state = "working" # working | blocked | idlepriority = 200region = "screen" # screen (recent output, default) or title (window title)any = ["esc to interrupt", "esc to cancel"]
[[rule]]state = "blocked"priority = 300region = "screen"all = ["do you want to proceed"]not = ["cancelled"]Each rule holds when all of the conditions you list are true:
| Field | Holds when |
|---|---|
any | any listed substring is present (case insensitive) |
all | every listed substring is present |
not | none of the listed substrings are present |
spinner | a running braille spinner glyph is visible on a line |
Priorities follow the built-ins: blocked prompts sit around 300, working hints around 100 to 200. Give a rule a higher number than the built-in you want it to beat. A file that fails to parse is skipped with a warning, so a typo never takes detection down.
Agents can drive artuditu
Section titled “Agents can drive artuditu”Inside every pane, artuditu injects $ARTUDITU_PANE_ID and $ARTUDITU_SOCKET_PATH, so
the agent in the pane can call the same API you do:
artu pane status # my own statusartu wait output 7 --match "tests passed" --timeout 300artu wait agent-status 7 --status doneSee Scripting artuditu for the patterns, and Multi-Agent Orchestration for running a whole team.