* Read Codex app-server replies from the raw fd (#13703) * Resolve Codex through mise which instead of running the lazy launcher (#13109) * Skip the Codex app-server probe when there are no credentials (#13106) Adapted: credentials are checked in the home being probed rather than in the CODEX_HOME environment variable, since each registered account is probed in its own home, so a signed-out secondary account isn't hidden behind the primary's login. A home without credentials reports "Waiting for auth" like any other signed-out home. The credentials store setting is read with tomllib, so a single-quoted value counts too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show the Codex CLI's own error when its app-server dies (#8977) Detect an app-server that exits or stops answering, and report the end of its stderr instead of a bare RPC method name. Rebased onto the raw-fd reply reader; the switch from "-a on-request" to "-a never" is left out, keeping the current approval flags. * Count pi sessions when HOME is a git checkout (#13209) * Count only OpenAI-backed native sessions as Codex usage (#12032) * Deduplicate Pi usage across forked sessions (#8602) * Skip unchanged native Codex token snapshots (#10531) * Count omp and pi profile sessions in the agent usage collectors (#9546) `omp --profile=<name>` (and pi's equivalent) relocates the whole agent tree under <base>/profiles/<name>/. The Claude and Codex collectors only ever scanned <base>/agent/sessions, so a subscription driven entirely through a profile was invisible to the agents panel: no tokens by day, no tokens by model, no prompt or session counts. Discover the profile roots alongside the default one. Sessions are keyed by file path, so a profile adds sessions instead of double-counting the default root, and a missing or unreadable profiles directory leaves the existing behavior untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014zFbJcDEEpV5BAmsH6kAB3 * Skip unrelated Codex session lines before JSON parsing (#12803) Adapted: session_meta lines also pass the pre-filter, since the provider filter from #12032 reads them to skip rollouts served by a non-OpenAI provider. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read only the Codex session files that changed since the last scan (#12595) Native Codex rollouts keep per-file totals between runs, replayed while a file's mtime and size are unchanged. Rebased onto the session_meta provider filter, snapshot dedup, and line pre-filter, which now live in the per-file reader. pi and omp sessions are left out of the per-file cache: a forked pi session repeats its parent's messages, so they are deduplicated across the whole tree on every scan. * Count streamed Claude messages by their highest-output usage line (#10606) Claude Code writes a streamed assistant response as several transcript lines that share one message id, one per content block. Each line carries a usage object. The first line's output_tokens is a placeholder, often 1, and the last line has the real count. Input and cache fields usually match across the lines. The scanner dedupes by message id and keeps the first line it sees, so it under-counts output tokens. On a machine with 2,577 transcripts it reported 39.0M output tokens against 60.1M used, a 35% shortfall. Input and both cache fields differed by under 0.01%. Keep the line with the highest output count, with the last one scanned winning a tie. The whole line is kept because a response can fall back to another model mid-stream. Those lines are separate snapshots with different cache figures and a different model, and taking a maximum per field across them over-counts cache tokens and credits the wrong model. The zero-usage check now runs before dedup, so a zero-usage first line no longer claims a message id and hides a later line with real usage. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: GPT-6 Astra <noreply@openai.com> * Index Claude transcripts so the agents refresh reads only what was appended (#8313) omarchy-agent-usage-claude re-parsed every line of every transcript under ~/.claude/projects on each refresh: no mtime cutoff, no memory of the last pass. The agents widget is on by default and ticks every 15 minutes, so the cost grew for the life of the machine. After one month here that was 803 files, 640 MB, 127k lines and 57k JSON parses per tick, about 1 core-second, pushed through the page cache every quarter hour forever. Keep a per-file index next to the scan cache: the unique usage records already parsed out of each transcript and the byte offset they end at. A file whose size and mtime match is not opened; a file that grew is read from the stored offset; a file that shrank or was rewritten is read from the start. --force drops the index and rescans from scratch. The summary is built from the indexed records in the same directory order the walk always used. That matters: when a resumed session carries earlier messages, the same message id appears in two files with different usage, and the first file visited wins. 91 ids differed on this machine; sorting the walk moved one model's output total by 25k tokens. Output is now byte-identical to the previous scan on a frozen copy of the corpus, cold, warm, and after an append. Warm refresh: 1.0 s -> 0.10 s of CPU, of which the scan itself is 70 ms; the index for this corpus is 2.9 MB. Adapted: - Rebased onto #10606: the highest-output rule for streamed messages now lives where the index parses records, and decides between files too. - The index records the timezone it was written in, and a change rereads every transcript, since its records hold local days. - A file only counts as appended to when its inode and the hash of what was already read still match, so a transcript replaced by a larger one, or rewritten in place, is read from the start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Label a Claude Team seat by its subscription, not its rate-limit tier (#11109) The collector built the plan label from the OAuth rateLimitTier first, so a Team premium seat, which runs on default_claude_max_5x, showed in the agents panel as "Max 5x". Lead with subscriptionType and keep the multiplier as its qualifier: Max still reads "Max 5x", a Team seat reads "Team 5x". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Label the Claude plan from the profile the CLI refreshes (#7225) Adapted: the profile is found the same way current_account_id() finds it, now shared as profile_path(): ~/.claude.json for the default home, the home's own .claude.json otherwise. The original fell back to ~/.claude.json for any home without CLAUDE_CONFIG_DIR set, so a secondary account read the primary's tier. The profile's tier also keeps the subscription in the label, so a Team seat stays "Team" (#11109). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Call a lapsed Claude access token paused, not signed out (#8093) * Refresh Claude usage after the clock moves backwards (#9956) * Bound unreadable Claude transcript warnings (#12414) * Count Claude usage from opencode v2 sessions (#13894) * Reload agent usage records when an inotify watch fails to rearm (#10067) * Reload agent usage records after each update run instead of on a timer Rather than #10067's two-minute timer per record, reload every record when the omarchy-agent-usage-update process exits, the moment its files can have been replaced. A reload that finds a file unchanged keeps its record, so the panel isn't stirred up by identical data. The grep test now runs the QML functions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show the agent status when the trouble line has no help text (#8497) * Clear stale agent login guidance after a successful probe (#8892) * Clear the Grok login hint after a successful probe #8892 cleared the default login hint after a successful probe in the Claude and Codex collectors; Grok's collector had the same stale hint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read Fireworks credentials from pi's auth.json (#7455) The Fireworks collector skipped pi, Omarchy's default agent, when walking its credential ladder, so a machine signed in to Fireworks only through pi (/login fireworks) never showed the tab. Insert the key pi stores in $PI_CODING_AGENT_DIR/auth.json (default ~/.pi/agent) between the firectl auth.ini and the opencode fallback. pi keys can be literals, $ENV_VAR/${ENV_VAR} references, or !command shell lookups. The collector resolves the first two; command lookups stay pi-only and are skipped rather than sent to the API verbatim. * Call a lapsed Grok access token paused, not signed out Grok's access token lives six hours and Grok mints a new one from its refresh token whenever it starts, so a lapsed one is routine. Reporting it as an expired sign-in made the panel offer Sign-in required several times a day, sending people through grok login for nothing. With a refresh token present it now reads as paused, like Claude's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep showing Grok's last limits while it sits idle While Grok hasn't run, nothing on the machine has spent its allowance, so with a refresh token on hand the last numbers still stand: they show as current rather than dimmed under a status line. A weekly window that reset in the meantime starts over at 0%, a whole number of weeks on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Ask for a Grok sign-in once its refresh token is past 30 days A refresh token older than Grok's 30-day sign-in can't renew anything, so the panel offers Sign-in required again instead of showing the last limits as current. With nothing cached yet it says to start Grok, rather than showing an empty section without a word. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check both ends of what the Claude index read before resuming a transcript A transcript rewritten in place could grow and change only after its first kilobytes, and the index took it for an append. It now compares the last kilobytes before the resume point too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Simplify the agent usage collectors - Codex: pass the forced-scan choice down instead of a module global, make the per-file reader's cache arguments required, shrink the cache record check, and drop guards for shapes that can't occur: an empty launcher path, realpath raising, mise itself being a lazy launcher, multi-line `mise which` output, and probing without a temp file for stderr. - Claude: decide an append by the digest of both ends of what was read alone; the inode and mtime checks it made redundant are gone. - Snapshot: the device id falls back to the hostname, which always exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Share fixture setup in the agent usage scanner tests Every fixture home lives under one scratch directory with a single cleanup trap, instead of a trap rewritten with a longer list for each new home, and the Codex test builds its signed-in homes with one helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Treat a replaced Claude transcript as new even when its ends match Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Probe Codex without its error text when there's no temporary space Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: tossbaws <17258053+tossbaws@users.noreply.github.com> Co-authored-by: surim0n <suritech@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: anonwurcod <anonwurcod@proton.me> Co-authored-by: Kevin Rajan <7121943+kvnloo@users.noreply.github.com> Co-authored-by: Nate Ashby <nate.ashby11@gmail.com> Co-authored-by: Aris Gysel <aris.gysel@me.com> Co-authored-by: Brams <76213579+Brams-s@users.noreply.github.com> Co-authored-by: This_Is_NPC <gabrielfollone27@gmail.com> Co-authored-by: sanjyay <102979855+sanjyay@users.noreply.github.com> Co-authored-by: PapistProtocol <12738904+PapistProtocol@users.noreply.github.com> Co-authored-by: steez <stevedimakos97@gmail.com> Co-authored-by: GPT-6 Astra <noreply@openai.com> Co-authored-by: Ryan Yogan <ryanyogan@gmail.com> Co-authored-by: Oli Denton <41393837+omdenton@users.noreply.github.com> Co-authored-by: Igor Kramar <i@ikramar.ru> Co-authored-by: Martin Eidensten <martin@meibe.se> Co-authored-by: Romain Perron <rdj.perron@gmail.com> Co-authored-by: Omarchy Contributor <contributor@users.noreply.github.com> Co-authored-by: manuaudio <manu@arimaka.com> Co-authored-by: Tyler South <tsouth2@gmail.com> Co-authored-by: whathek <Hek846@users.noreply.github.com> Co-authored-by: Ty Richards <me@tyrichards.com>
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 writes 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
Every subscription on one page, limits first.
- Hero — the agents robot, and a line that rotates through what the token counts add up to across every agent: tokens this week and today, the most used model, the busiest day, and today's prompts and sessions. Its corner has + to add a subscription and >_ to start the default agent.
- One section per agent — its mark, name, and plan, then a compact line per limit window: its meter and the time until it resets (the exact percentage on hover). A model-scoped allowance on the same clock (Claude's Fable weekly limit) is a tick on that window's meter rather than a line of its own; the row's tooltip names it. Sign-in and endpoint trouble shows under the name in the urgent color. Limits kept from an earlier check after a failed one dim, and their tooltip says how old they are.
- Accounts — an agent with more than one subscription account (see
omarchy agent account) lists each: name and plan on one line (the email on hover), and its own limit lines. An ACTIVE label marks the account new sessions start as; the others get a Use link. Hovering the line also reveals Autoswitch, which moves new sessions over on their own once the active account reaches its threshold; while it's on it stands in for Use, which shows only when you're on the line, and clicking it again goes back to notifying. Click a name to rename the account in place. - Balance — prepaid agents show a credit ledger instead of limits: a fuel-gauge meter that drains toward empty, the remaining credit, and funded-versus-spent detail.
- Make something cool — starter prompts (a new theme, plugin, or app) that
start the default agent on the task through
omarchy agent prompt. - Adding a subscription — the + in the hero's corner swaps the page for
Claude Code, Codex, and Grok as large marks, three across, with the first
one focused, and the hero's line reads Add an account. The + becomes the
X that goes back. An agent that can't be added is dimmed and says why on
hover. A further account asks for a name first, and Enter signs it in. The
panel then runs
omarchy-agent-account-add --eventsand follows it: the status, the code Grok asks you to confirm in the browser, a field to paste Claude's code back if its page shows one instead of finishing, and a link to reopen the sign-in page. Esc or the X stops the login. The browser taking focus may close the panel; the sign-in carries on and its result arrives as a notification.
The icon is always in the bar. On a machine with no agent yet, the panel is
the blank slate for setting one up: it opens on the same choice of Claude,
Codex, or Grok, and the first agent signed in becomes the default agent if
none was picked. An agent appears once it is enabled in settings and has
recorded usage, on this machine or a synced one; a CLI installed
mid-session shows up at the next refresh. Drop the widget with
omarchy plugin disable omarchy.agents.
Data
Each agent is one JSON record in ~/.local/state/omarchy/agents/usage/,
written by omarchy-agent-usage-update. That command runs one
omarchy-agent-usage-<agent> collector per agent; the widget invokes it
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 on the built-in openai provider (plus pi and opencode sessions) |
grok |
The credits endpoint behind Grok's /usage view (the billing period's included usage) |
Each session's usage.json (the ledger grok usage prints: tokens by model per finished turn), plus summary.json for sessions |
fireworks |
Estimated prepaid balance: configured funding minus rated account costs | Fireworks billing API, grouped by day and model for the last 30 days |
When ~/.local/state/omarchy/agents/accounts/<claude|codex|grok>.json
registers more than one account, the claude, codex, and grok records
also carry
accounts: [{ id, label, email, plan, active, limits, stale, usageStatusText, authHelpText }], each account probed with its own sign-in (Claude caches each
account's limits separately; Codex runs one app-server per account home), and
accountSwitch: { mode, threshold }. The record's top-level limits and
tierLabel keep describing the active account, and local stats stay one set,
since every account shares the primary home's history. After each run,
omarchy-agent-usage-update hands the fresh limits to
omarchy-agent-account-state autoswitch, which notifies or switches when the
active account crosses its threshold, and re-collects the record if the active
account changed.
Codex CLI will front any OpenAI-compatible backend — --oss, or a custom model_provider in config.toml aimed at Ollama, LM Studio, or a gateway — and those rollouts sit in the same sessions directory as OpenAI-backed ones. The Codex collector skips a rollout whose first session_meta.model_provider names anything but the built-in openai provider. Rollouts written before Codex recorded that field carry no provider and still count.
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, Grok via GROK_HOME. Grok's
plan comes from the settings it caches in its home, and its limit from the
credits endpoint its own /usage view reads, asked with each account's
sign-in; a sign-in left to lapse shows the last credits until Grok runs
again. Fireworks reads
FIREWORKS_API_KEY and FIREWORKS_ACCOUNT_ID first, then
~/.fireworks/auth.ini (which firectl set-api-key creates), then the key
pi stores in ~/.pi/agent/auth.json when Fireworks is signed in there
(honoring PI_CODING_AGENT_DIR, and pi's literal and $ENV_VAR key forms),
and finally the key opencode stores in ~/.local/share/opencode/auth.json.
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 = refresh. It turns urgent when any account new sessions use is at 90% of a window, or a prepaid balance is down to its last 10%.
- Panel: the arrows (or
h/j/k/l) walk a cursor over everything that does something, row by row: the hero's buttons, each agent's header, each switchable account (landing on Use, with Autoswitch to its left), and the starter tiles, or the agents to add. Ctrl+Up/Down (or Ctrl+k/j) moves the agent the cursor is in up or down the page; dragging an agent by its mark does the same, lighting the header it will land on. The order is kept in~/.local/state/omarchy/agents/order.json. Hovering moves the same cursor. Enter acts on it, or refreshes when nothing is lit;rrefreshes, Tab moves to the neighboring bar panel, Esc closes. - Accounts:
1–9jump to an account across every agent, and Enter makes it active (picking alone never switches).mtoggles automatic switching for the picked account's agent. While an agent with several accounts has its active one within 15 points of its switch threshold (80% at the default), the limits refresh every three minutes. - IPC:
omarchy-shell omarchy.agents <open|close|toggle|refresh>.
Settings
Settings live in the widget's entry in ~/.config/omarchy/shell.json. The
top-level keys can be set with
omarchy bar set omarchy.agents <key> <value>:
| Key | Default | What it does |
|---|---|---|
refreshIntervalSec |
900 |
How often the usage records regenerate |
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 omarchy.agents refreshIntervalSec 300 --json
omarchy bar set omarchy.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 omarchy.agents providers '{
"claude": { "enabled": true },
"codex": { "enabled": false },
"fireworks": { "enabled": true }
}' --json
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.