Agents
One bar icon and one panel for every AI coding subscription on the machine.
The panel is strictly a display: it watches the usage records that
omarchy-agent-usage-update and the OpenCode clone collector write to
~/.local/state/omarchy/agents/usage/ and draws whatever appears there.
Panel.qml owns the bar button and the popup; Main.qml discovers and watches
the records (and handles the optional cross-device aggregation); Agent.qml is
the per-record file watcher.
Panel
- Hero — the mark, the tool, and the plan it runs on ("Max 20x", "Pro"). Auth and endpoint problems replace the plan line and repeat in a card.
- Subscription switch — one chip per enabled agent (
h/lor click). It appears only when more than one agent is enabled. - Limits — the percentage of each allowance used, a matching meter, and the time until the session or weekly window resets.
- Balance — prepaid agents report a credit ledger instead of limits: remaining credit, a fuel-gauge meter that drains toward empty, and funded-versus-spent detail.
- Tokens by day — one row per day for the last week: day, bar, tokens (and optionally cost), with today bolded at the bottom. Hover a row for its prompt/session count and cost.
- Tokens by model — tokens per model (and optionally cost) with the bar behind each row scaled to the heaviest model, the same way the weekly chart scales to its busiest day. Hover for the input / output / cache split and cost.
Both the Tokens by day and Tokens by model sections obey the
modelDisplayCost setting: tokens shows only token counts, hybrid shows
tokens (cost), and cost shows the dollar amount only. Cost data is read
from the OpenCode collector; Claude, Codex, and Fireworks fall back to tokens
because their system collectors do not expose per-request pricing.
A subscription appears only when it is enabled in settings and has actually recorded usage — on this machine or on a synced one. With one such agent there is no switch row at all; with none, the module leaves the bar entirely rather than sitting there with nothing to say. A CLI installed mid-session shows up at the next refresh, so nothing polls the disk waiting for it.
That self-hiding is why the widget ships in the default bar layout: a machine
that has never run an AI coding agent draws nothing, and the icon arrives on
its own the first time a scan finds usage. Drop it with
omarchy plugin disable tomv.agents.
Data
Each agent is one JSON record in ~/.local/state/omarchy/agents/usage/,
written by omarchy-agent-usage-update or, for this clone, the bundled
OpenCode collector. The built-in command runs one
omarchy-agent-usage-<agent> collector per agent; the widget invokes it
alongside the OpenCode collector on its refresh timer and whenever you ask for
a refresh, and picks up any record that lands in the directory regardless of
who wrote it.
Adding an agent therefore never touches this plugin: ship a collector that
prints the record contract (see the claude and codex collectors in
bin/), and the panel gains a tab. An assets/<id>.svg mark is optional —
with an assets/<id>-light.svg twin if the mark needs a dark variant for
light surfaces — and the bar glyph stands in when there is none.
| Collector | Limits | Local stats |
|---|---|---|
claude |
Anthropic's OAuth usage endpoint (5-hour session + 7-day weekly) | ~/.claude/projects transcripts, opencode sessions on an Anthropic provider, plus stats-cache.json and history.jsonl as fallback |
codex |
The Codex app-server RPC | native Codex CLI session files (plus pi and opencode sessions) |
fireworks |
Estimated prepaid balance: configured funding minus rated account costs | Fireworks billing API, grouped by day and model for the last 30 days |
opencode-go |
OpenCode Go API (5-hour, weekly, and monthly windows) | Go assistant messages from opencode.db, grouped by day and model |
opencode-zen |
Zen free-model daily quota, read reactively from opencode errors and probed with a free model only while limited | Zen assistant messages from opencode.db, grouped by day and model |
Claude limits need a signed-in CLI; without credentials the panel says so and
falls back to local stats only. A non-default Claude directory is honored via
CLAUDE_CONFIG_DIR, Codex via CODEX_HOME. Fireworks reads
FIREWORKS_API_KEY and FIREWORKS_ACCOUNT_ID first, then
~/.fireworks/auth.ini (which firectl set-api-key creates), then the key
opencode stores in ~/.local/share/opencode/auth.json when Fireworks is
signed in there.
OpenCode Go limits use the opencode-go key in
~/.local/share/opencode/auth.json and the
https://opencode.ai/zen/go/v1/usage endpoint. The Zen provider's local usage
is shown in a separate OpenCode Zen tab.
OpenCode Zen limit
The Zen tab also reports a hard block when the free-model daily quota is hit.
It is detected reactively from opencode's own records: the collector reads
the newest opencode assistant message in opencode.db — either a message
whose error is a Zen limit (FreeUsageLimitError, RateLimitError,
CreditsError, monthly limits), or the empty-response signature opencode
leaves behind when a free model is silently rejected (zero tokens, no finish).
Nothing is probed in normal rhythm.
While a block is active, each refresh probes once with a free Zen model
(big-pickle, with deepseek-v4-flash-free/mimo-v2.5-free as fallbacks) to
confirm recovery and read the live Retry-After the 429 error carries. The
free-model quota always resets at the next UTC day, so the countdown starts
from that even before a probe lands. The panel shows the block as a full,
urgent "Rate limited" window with a live "Resets in …" countdown and the bar
icon alarms while it lasts.
Notifications
Limit notifications are enabled by default for every provider that reports limits. The thresholds are 80%, 90%, and 95%. Each threshold notifies once per provider and shell session; a reload or percentage change cannot spam the same threshold, while a new shell session can notify again. The bar's existing visual alarm remains at 90% and uses the current theme's accent/urgent colors.
Fireworks balance
The collector first asks the account's :getBalance endpoint for the real
prepaid ledger. That endpoint exists but is permission-gated, and as of
August 2026 no console-issued API key passes it — Fireworks appears to
reserve it for the dashboard session. The probe stays because it is cheap
and the live figure lights up automatically if Fireworks ever opens it to
keys. Until then the collector falls back to estimating the balance from
configuration in ~/.config/omarchy/agents/fireworks.json:
{
"accountId": "",
"fundedAmount": 20,
"fundedAt": "2026-07-01"
}
Set fundedAmount to the credits purchased and optionally fundedAt to the
purchase date; with no date, the collector uses the account creation time. It
subtracts rated account costs and the panel labels the result as estimated.
For a later top-up, increase fundedAmount by the new credit while keeping
the original fundedAt, so both the funding and spend still cover the same
period. accountId only matters when one API key can access several
accounts. Without a configured fundedAmount the tab still shows token
usage, just no balance. With a live ledger, fundedAmount is optional and
only adds the meter and the spent-of-funded line under the real figure.
Interactions
- Bar icon: left = panel, right = launch agent, middle = next subscription.
- Panel:
h/lswitch subscription,j/kscroll,ror Enter refresh, Tab moves to the neighboring bar panel, Esc closes. - IPC:
omarchy-shell omarchy.agents <open|close|toggle|refresh|next>.
Press s inside the panel to open the settings TUI. Use n for the global
notification switch, 1 through 5 for Claude, Codex, Fireworks, OpenCode Go,
and OpenCode Zen, and w/a/c to cycle the three notification thresholds.
Press s or Escape to return to the dashboard.
Settings
Settings live in the widget's entry in ~/.config/omarchy/shell.json. The
top-level keys can be set with
omarchy bar set tomv.agents <key> <value>:
| Key | Default | What it does |
|---|---|---|
refreshIntervalSec |
900 |
How often the usage records regenerate |
notifyEnabled |
true |
Enable desktop notifications for limit thresholds |
notifyWarnPercent |
80 |
Warning notification threshold |
notifyAlarmPercent |
90 |
Critical notification threshold |
notifyCriticalPercent |
95 |
Final notification threshold |
notifyClaude, notifyCodex, notifyFireworks |
true |
Enable notifications for the corresponding provider |
notifyOpenCodeGo, notifyOpenCodeZen |
true |
Enable notifications for the corresponding provider |
modelDisplayMax |
6 |
Maximum number of models shown in the model breakdown |
modelDisplayMinTokens |
0 |
Hide models whose total token count is below this threshold |
modelDisplayCost |
"hybrid" |
tokens, hybrid (tokens + cost), or cost (cost only). Cost is read from the OpenCode collector for models and daily rows when available; other agents fall back to tokens. |
syncMode |
"Off" |
"On" writes this machine's snapshot and merges the others |
syncDir |
"" |
A folder synced by Syncthing, Dropbox, rsync, … |
syncFileName |
<hostname>.json |
This machine's snapshot file |
syncDeviceId |
hostname | Stable device name inside the snapshot |
Numbers need --json, or they land in shell.json as strings:
omarchy bar set tomv.agents refreshIntervalSec 300 --json
omarchy bar set tomv.agents syncDir '~/Sync/agent-usage'
Per-agent enablement is nested, and set writes its key literally rather
than walking a dotted path — so pass the whole providers object as JSON (or
edit shell.json directly):
omarchy bar set tomv.agents providers '{
"claude": { "enabled": true },
"codex": { "enabled": false },
"fireworks": { "enabled": true },
"opencode-go": { "enabled": true },
"opencode-zen": { "enabled": true }
}' --json
The notify field can also be set per provider inside providers, for
example "codex": { "enabled": true, "notify": false }.
enabled defaults to true for every discovered agent; set it to false to
hide a subscription that is installed. Disabled agents are also skipped when
the records regenerate.
With syncMode on, every *.json snapshot in syncDir is merged, so today,
the last 7 days, and the all-time totals cover every machine you code on —
active days are unioned by date rather than summed. Rate limits stay
per-account and are never merged. A record may declare "scope": "account"
when its stats are account-global rather than machine-local (Fireworks'
billing API); those merge by taking the widest value instead of summing, so
the same account synced from two machines is not counted twice.
One caveat on "all-time": the Codex collector only reads native session files touched in the last 30 days, and Fireworks requests the last 30 days from its billing API, so their totals and day counts cover that window. Claude's cover every transcript still on disk.