Skip to content

Agents

wrkstrms runs AI coding agents such as Claude Code and Codex in ordinary panes, and keeps track of what each one is doing. This page explains how an agent’s status is worked out, how agents are labeled, how to get to the agent that needs you, and how to find out why an agent shows the status it does.

The prefix key is Ctrl-Space: prefix a means press Ctrl-Space, let go, then press a. Every key is in Keys and mouse.

An agent is any pane running an agent CLI. Start one the way you start any program:

Terminal window
ws claude --model opus # a new ws (or a new tab in the running one)

Inside wrkstrms, you can also split a pane (prefix v or prefix s) and run claude or codex in the new shell. Workstream templates can start agents for you; see Workstreams. Projects start agents with a prompt that tells them what to work on; see Projects.

Each pane gets WS_SOCKET, WS_PANE_ID and WS_WORKSPACE in its environment. The agent’s hooks use them to report back to the wrkstrms that owns the pane.

Agents that ws starts from a template get a short summary of what ws remembers for their repo, workstream and project: preferences, decisions, tasks, the latest handoffs and recent sessions. It goes to Claude Code as an appended system prompt and to Codex as developer instructions. See Memory.

Claude Code and Codex sessions you start yourself get the same summary through their SessionStart hook, as do sessions after /clear or compaction. If you installed ws’s hooks before this, run ws integrate claude again (it adds PreCompact; ws doctor says when hooks are missing).

To move a pane’s work to another agent (Claude to Codex and back, or any CLI you add), use prefix A or ws handoff --to <agent>. See Memory.

With ws integrate claude --mcp (and codex --mcp), agents can also search memory and save to it as they work, through ws’s MCP server. See Memory.

Every agent has one of four statuses. They are ordered by urgency, and lists that sort by status put the most urgent first.

Glyph Status Meaning
● (red) blocked Waiting on a permission prompt or a question
✓ (green) done Finished a turn you haven’t looked at yet
◐ (yellow) working Running
○ idle Finished and seen, or not started

A done agent turns idle once you focus its pane.

Status comes from the agent’s own hooks. Install them once for each agent CLI you use:

Terminal window
ws integrate claude # hooks in ~/.claude/settings.json
ws integrate codex # hooks in ~/.codex/hooks.json
Flag Effect
--print Print the resulting settings file without writing it
--remove Remove the ws hooks instead of installing them
--bin <BIN> The ws binary the hooks run (default: the one you ran integrate with)

What ws integrate does:

  • It adds one hook per event. Each hook runs ws report --agent <claude|codex> <Event> with a 5-second timeout.
  • It keeps every other setting and every hook that isn’t from ws. Running it again replaces the old ws hooks, even if they point at a different ws binary.
  • It refuses to touch a file that isn’t valid JSON.
  • When it changes the file, it keeps a backup next to it, named settings.json.ws-backup-<timestamp> (or hooks.json.ws-backup-<timestamp>).
  • For Codex, the hooks file is $CODEX_HOME/hooks.json when CODEX_HOME is set.

Codex only runs hooks when its hooks feature is on. ws integrate codex asks codex features list. If Codex doesn’t report the feature as on, ws sets features.hooks = true in config.toml next to hooks.json, keeping the rest of the file and a backup. --remove leaves that setting alone, because other hooks may need it.

It also turns off Codex’s daemon_auto_start feature (features.daemon_auto_start = false). With it on, every Codex window talks to one shared background daemon, and Codex runs hooks in that daemon, with the environment of the pane that started it. Every Codex session’s status would land on that one pane, or be dropped if that pane is gone. If the daemon is already running, ws integrate codex and ws doctor say so. Stop it with codex app-server daemon stop, which ends the sessions using it, and start Codex again (codex resume picks a conversation back up).

Agents that were already running pick up the hooks when you restart them. ws doctor checks that the hooks are installed and point at a ws that exists.

The hooks are safe outside wrkstrms. ws report reads the hook’s JSON from stdin and sends it to the wrkstrms that owns the pane. Outside wrkstrms it does nothing. It always exits 0 and gives up after one second, so it can’t break or stall the agent.

Point the hooks at a ws binary that stays put. If you build ws from source, pass --bin with the path of an installed copy.

These are the events ws installs hooks for:

Agent Events
Claude Code SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Notification, PreCompact, Stop, SessionEnd
Codex SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PermissionRequest, Stop, Interrupt, SessionEnd

Each event moves the agent to a status:

Event Status Also records
SessionStart idle Session id, transcript path, model
UserPromptSubmit working The prompt (the first one is kept for the label)
PreToolUse, PostToolUse working The tool, e.g. Edit user.rb
Notification (Claude), PermissionRequest (Codex) blocked What the agent is asking, e.g. “Claude needs your permission to use Bash”
Stop done
Interrupt (Codex), SessionEnd idle

Codex sends Interrupt when you stop a turn. The agent goes idle, but its session goes on: only SessionEnd ends a project session.

Two details:

  • Claude Code also sends a Notification when it has sat waiting for your next prompt (“waiting for your input”). If the agent is already done or idle, ws ignores that reminder instead of marking the agent blocked.
  • Any other event is ignored, but it still tells ws that the agent’s hooks work.
  • A SessionEnd from a pane with no agent is ignored rather than making one. An agent shut down with its server can report after ws restart, carrying a pane number the new server has given to another program.
  • The Dashboard page is never an agent, whatever reports from its pane.

wrkstrms also checks each pane’s foreground process every 2 seconds. When a pane runs a known agent CLI, it shows up as an agent even before its hooks report, or without hooks at all. When the pane is back at a shell prompt, the agent is dropped.

These command names are recognized, including when they run under node, bun, deno or any Python (python3, python3.12, macOS’s Python):

Command Agent
claude Claude Code
codex Codex
opencode OpenCode
aider aider
gemini Gemini CLI
goose goose
amp Amp
cursor-agent Cursor agent
qwen Qwen Code
crush Crush
ollama run <model> A local model through Ollama

An agent found only by its process name has no status until its hooks report. Its detail line says so instead of showing a status:

  • For Claude Code, it shows the command that fixes it: claude · no status: ws integrate claude.
  • For Codex, it says codex · no status: see ws doctor. Codex’s hooks can be installed and still not reach ws, when its shared daemon runs them; ws doctor tells the cases apart.
  • For other agents, it shows no status hooks. Only Claude Code and Codex have hooks today.

Every agent has a short label, shown in the sidebar, pane frames, the finder and notifications. The label comes from the best source available:

Priority Source Example
1 A label you set “Fix dropdown a11y”
2 The agent’s terminal title, with any leading spinner or status glyph removed “Refactoring Button variants”
3 The first line of the agent’s first prompt “The focus ring on Button is missing whe…”
4 The pane’s command claude

Automatic labels are cut to 40 characters. A title that only names the program (“Claude”, “Claude Code”, “Codex”, “OpenCode”) doesn’t count, so the first prompt is used instead.

To set a label, press prefix . in the pane, or r on the agent in the sidebar, or choose “Label pane…” from the pane’s right-click menu. Type the label and press Enter. An empty label goes back to the automatic one. Your label wins over every other source and is saved with the session.

An agent needs you when it’s blocked or done. Press prefix a or Alt-a to jump to the one that has needed you longest, blocked agents first. Press it again to move on to the next. If no agent needs you, the top bar says “no agents need you”.

Status also shows up everywhere else:

  • Top bar: ● N need you counts blocked agents and shows the key that jumps there; clicking it jumps too. Working and done totals follow it.
  • Tabs: each tab shows the most urgent status of its agents.
  • Pane frames: the frame’s top border shows the agent’s status glyph and label, with the detail line on the right.
  • Sidebar: see The AGENTS section.
  • Finder: prefix f, then type agent to list only agents. With an empty query, the finder lists agents first, most urgent first.

When an agent becomes blocked and you can’t see its pane, wrkstrms sends a desktop notification titled “ws” with the agent’s label and what it’s asking. A pane counts as seen when it’s in the tab on screen, and, if that tab is zoomed, when it’s the zoomed pane.

Notifications are throttled. After one is sent, the next waits at least 5 seconds, and everything that piled up in the meantime goes out as one alert: “3 agents need you: …”.

To turn notifications off, set WS_NO_NOTIFY (to any value) in the environment ws starts in:

Terminal window
WS_NO_NOTIFY=1 ws

The sidebar’s AGENTS section lists every agent in every workspace, sorted by urgency: blocked, then done, working and idle. Within a status, the one that has waited longest comes first. The heading shows how many agents there are.

Each agent takes two lines:

  • The first line has the status glyph and the label. At the right end, a working agent shows a sparkline of its recent output, and a blocked or done agent shows how long it has waited (12s, 3m, 1h5m).
  • The second line shows the agent kind and model (claude·opus), then what it’s doing: the question it’s asking, the tool it’s using, done, not seen, or the “no hooks” hint.

Blocked agents get a red background.

With the sidebar focused (prefix b):

Key Action
j / k Move
Enter Jump to the agent’s pane
i Open the status inspector for the agent
r Label the agent’s pane
x Close the agent’s pane (asks first)
z or Tab Fold or unfold the section

With the mouse, click the heading to fold the section, drag the heading (═) up or down to resize it, and scroll it with the wheel. While a sidebar filter is active, the AGENTS section is hidden. See The interface for the rest of the sidebar.

When an agent’s status looks wrong, the inspector shows why it has that status. Open it in any of these ways:

  • i on the agent in the sidebar
  • prefix I for the focused pane
  • “Inspect agent status” in a pane’s or tab’s right-click menu

The inspector’s title names the workspace, tab and pane number. It shows:

Row What it shows
status The status, how long it has held, and what set it: a hook event, a process detection, or you
asking What a blocked agent is asking
tool The tool a working agent is using
identity Agent kind, model, session id, and whether hooks are reporting
label The label, and whether it was set by you, taken from the first prompt, or from the title or command
process The pane’s foreground process and up to four of its children

Below the facts is a timeline, newest first, of the pane’s recent entries. Each entry has a UTC time and a kind:

Kind Meaning
hook A hook event arrived, with the tool, message or prompt in a few words
status The status changed, and which hook changed it
detect The agent was found by process name, or its pane went back to a shell
override You set the status by hand
handoff ws asked the agent for a project handoff entry

Keys in the inspector:

Key Action
d / w / b / x Set the status by hand to done / working / blocked / idle, until the agent’s next real event
c Copy a bug report to the clipboard and save it
r Refresh
j / k Scroll the timeline
Esc, q or i Close

The bug report is plain text: the ws version, OS and architecture, the facts and the full timeline. Prompts in it are cut to their first line. It’s saved as status-report-<pane>-<timestamp>.txt in ws’s state directory (~/.local/state/ws/, or $XDG_STATE_HOME/ws/).

The same events are saved to disk, so you can look at them after the fact, with or without the server running.

Terminal window
ws debug events # every pane, the last 10 minutes
ws debug events --since 2h # further back: 90s, 10m, 2h, 1d
ws debug events --pane 3 # one pane (its number is in the inspector title)
ws debug status 3 # pane 3's saved identity and its last 20 events

ws debug events prints one line per event, oldest first: the time (UTC), the pane, the kind and the detail.

13:00:05 pane 3 status → blocked (hook Notification)
13:04:12 pane 3 hook SessionEnd of another session (4f1c…), ignored

That second line is Claude Code ending a short side session of its own from the same pane. It doesn’t end the agent’s session, so ws leaves the pane’s status, session and memory alone.

ws debug status <pane> prints the pane’s command and directory, the agent’s kind, model and session id, its status as last saved, and its last 20 events.

Both read the state file of the default server, or of a named one with -L <name>. See Troubleshooting for more on fixing status problems, and CLI reference for every command.