---
title: Stats — Cost, Tokens, Sessions, and Trends
---
# Stats — Cost, Tokens, Sessions, and Trends

## What it is

> **Library page** — describes what users see and how to use it. Self-contained so an outside AI (with no repo access) can read this and answer "what is Stats and how do I use it?"

Stats is a built-in Omniscio virtual project that surfaces aggregate metrics across every Claude Code session you've run: total spend, token usage, LLM time and active time, per-project breakdowns, and trend lines over time. It lives in the Omniscio group of the project sidebar, between **AI Coaching** and **Settings**.

Before 2026-04-30 these metrics lived inside Settings → Statistics. Settings is for configuration; data exploration deserves its own pane, so Stats was extracted into its own virtual project.

### What it shows

These tabs, switchable from the sub-sidebar on the left:

- **Overview** — usage value, total tokens (input/output), LLM time, active time, session count, **Your Messages** (just the user-typed ones), **Automated Actions** (combined count of in-session auto-replies + channel-driven Automations), **Interruptions Avoided** (a running tally of the times Omniscio handled something so you weren't interrupted — see "Interruptions Avoided" below), then a per-project table sorted by cost, then a Top Sessions list (cycle through metrics: cost, tokens, duration, etc.). The headline money tile is labeled **Usage Value**, not "Total Cost" — see "Usage Value vs money you actually paid" below for why. Cost values on that tile and inside the per-project table are rendered in compact `$X.XK` form once they cross $1,000 (rounded to the nearest $100); below $1,000 they show as raw integer dollars (e.g. `$87`). Exact sub-dollar values still appear elsewhere — e.g. the "avg/session" sub-line under Usage Value.

  Session counts and long durations get compact formatting too:
  - **Session counts** (the **Sessions** tile and the per-project "X sessions" badge) — below 1,000 they show as raw integers (e.g. `42`); at 1,000 and above they show as `X.XXXK` (divided by 1,000, rounded to the nearest 0.001, trailing zeros trimmed). So `2652 → 2.652K`, `2000 → 2K`, `1234 → 1.234K`.
  - **Long durations** (the **LLM Time** / **Active Time** tiles and the per-project **Active** / **LLM** cells) — below 10 hours they keep the existing `Xh Ym` / `Xm` / `Xs` format (e.g. `9h 59m`, `45s`); at 10 hours and above they round to the nearest hour and add thousands separators (e.g. `207h 28m → 207h`, `2198h 10m → 2,198h`). The per-session subtitles (e.g. "avg/session") stay in the precise compact format since those values are rarely that large.

- **Spend** — the central "where is my AI money going?" view. One sorted list, biggest first, with a grand total at the top, combining your **coding sessions** (every engine — Claude, Codex, etc. — as one "Coding sessions" line) with the ~30 background AI features that log to the cost ledger (Inbox Pilot, Plain Speak, AI Suggestions, Session Titles, Context Transfer, Daily/Weekly summaries, Email Summarizer, Automations, Voice, Calendar, Knowledge Base, and more). Each row shows its dollar amount, its share of the total, and a proportional bar. Honors the Time Range selector. Engine spend is counted exactly **once** (see "How the Spend tab is computed" below). For most people coding sessions are the overwhelming majority — the background features are a small sliver, which the bars make obvious at a glance.
- **Feature Usage** — counts of registered features (cron jobs created, recipes run, AI titles generated, etc.) tracked via the `feature_events` table. One such card is **App Opens**, which counts each time Omniscio is opened, split by device — `mobile` (a phone/tablet over web access), `desktop_web` (a computer browser over web access), and `desktop` (the desktop app) — so you can see how often Omniscio is used on mobile vs. desktop. It counts _opens_ (each page/app load), not time-in-app, and records only the device bucket, never the browser identity.
- **Inbox** — Inbox Attention Analytics: a 100% local report on how you triage the inbox — average time-to-clear, average items waiting (event-sourced depth), looked-but-didn't-act, and per-item view time, broken down by type / project / session, plus a per-day depth strip and an actions-taken breakdown. Honors the Time Range selector; on by default (Settings → Diagnostics toggle). See [inbox-analytics.md](inbox-analytics.md).
- **Shortcut Efficiency** — a passive stats tab surfacing keyboard-shortcut adoption. See [shortcut-efficiency.md](shortcut-efficiency.md).
- **Trends** — line charts showing cost / tokens / sessions over time.
- **Usage** — the four-chart rate-limit forecast (per-account usage rates, projected exhaustion, hour-of-day model). See [usage-forecast.md](usage-forecast.md).
- **Records** — a gamified "trophy case" of ~18 all-time personal bests (longest session, priciest day, longest streak, lifetime token/session tiers, automation leverage, and a Peak Parallelism family) that celebrate with tier badges + confetti when broken. See [records.md](records.md).
- **Nudges** — appears only when **Feature Discovery Nudges** is enabled (off by default); per nudge you've been shown, it puts the pitched feature's usage **before** the nudge next to **after** it, plus the shown → accepted/dismissed funnel. See [feature-discovery-nudges.md](feature-discovery-nudges.md).

The sub-sidebar also has a **Time Range** selector that filters Overview, Spend, and Feature Usage:

- **Today** — sessions/events from local midnight to now.
- **Yesterday** — sessions/events bounded between midnight yesterday and midnight today (the only range with both a lower and an upper bound).
- **7 Days** / **30 Days** / **90 Days** — rolling windows ending now.
- **All Time** — no filter.

"Today" and "Yesterday" use your local-day boundaries (the JS `setHours(0,0,0,0)` of the current Date, then converted to UTC for the SQL filter), so the labels match the day shown on your wall clock.

The Trends tab uses the same Time Range selector but treats it as a window-length anchored on today: Today = 1 day, Yesterday = 2 days (yesterday + today, since the trends endpoint always anchors to today), 7d/30d/90d as expected, and All Time maps to 365 days (the existing one-year cap on the trends endpoint).

The **Records** tab is **all-time** and ignores the Time Range selector entirely — the control is hidden when Records is active (a "most sessions ever in flight at once" trophy doesn't make sense scoped to "Today"). The **Usage** forecast tab likewise doesn't use the Time Range selector.

Note: For the **Feature Usage** tab, per-item features (such as snippet usage counts and automation run counts) only have an all-time total — there is no day-by-day history for them. When you pick a non-"All Time" range, those features show 0; events-sourced features (gmail, snooze, etc.) honor the filter normally.

A refresh button (top-right of the sub-sidebar) re-fetches the data. It spins only for a refresh you asked for — the background warm below never touches it.

**Tabs load ahead of you.** Opening Stats starts quietly loading every *other* tab's data in the background, so switching tabs is instant instead of showing skeletons while a fresh query runs. The warm is deliberately unhurried — it waits for an idle moment and loads one tab at a time — so it can never slow down the tab you're actually reading. Changing the Time Range starts a fresh warm for the new range, and leaving Stats stops it. Whatever hasn't warmed yet simply loads normally when you click it, exactly as before.

## Where to find it

1. Open Omniscio.
2. In the project sidebar, scroll to the Omniscio group (the section under the "Omniscio" divider).
3. Click **Stats** (chart icon, between "AI Coaching" and "Settings").

The Stats panel opens. There is no separate modal anymore — the Settings → Statistics tab is gone.

## How it behaves

### Usage Value vs money you actually paid

The big dollar figure at the top of Overview (and the "All AI usage" card on the Spend tab) is **API-equivalent usage value**, not a bill. It answers "what would all this AI work have cost at metered API rates?" If you run your coding sessions on a subscription account (a Claude plan you signed into, a ChatGPT plan behind Codex, etc.), that work is already covered by the flat monthly fee — the dollar figure is what it _would_ have cost, not what you paid. That's why a heavy user can see a five-figure Usage Value while having paid only their subscription. The number is genuinely useful (it's how you know your plan is paying for itself), but it is not money leaving your pocket.

The **out-of-pocket** number is the honest "money you were actually billed" subset: charges made against a real API key (your own Anthropic/OpenAI/etc. key, billed per token) rather than a signed-in subscription plan. You see it in two places:

- **Overview → Usage Value tile** — when part of the total is real out-of-pocket money, the sub-line reads "$X paid out of pocket, the rest covered by your plans." When there's no out-of-pocket spend to distinguish, it shows the usual avg/session sub-line.
- **Spend tab** — a dedicated **Out of pocket** card next to the "All AI usage" grand total, with the sub-line "Billed to your own API keys, not covered by a plan."

How the split is classified: session spend counts as out of pocket when the session ran on a real API-key account, OR on an own-key metered vendor engine (DeepSeek, Kimi, GLM, MiniMax, Meta, CrofAI) — those bill your real per-token vendor key even though they carry no signed-in account, so they're real money. Sessions on signed-in subscription accounts are plan-covered, as are non-metered sessions with no recorded account (a Codex run on a ChatGPT plan, Gemini, and the like). Background feature charges in the cost ledger count as out of pocket unless they were made by a signed-in subscription account. Spend preserved from purged sessions keeps its original classification; purged background-feature spend can no longer be classified and is counted plan-covered. The out-of-pocket figure is always between $0 and the total.

Note on freshness: the **message counts** (Your Messages / Automated Actions, and the per-project counts) are kept as a per-session running tally that a background task refreshes shortly after activity, so the Overview loads instantly even across a very large history instead of re-scanning every message. When the definition of a count changes, the app clears every session's tally, so the Overview falls back to reading the messages directly — correct straight away, just slower — while the background task refills it. That is how the 2026-09-09 "Your Messages" correction reaches an existing install, with no separate backfill step to run. In practice the numbers are current; during heavy, continuous chatting a count can lag by up to about a minute before it catches up. Every other metric (cost, tokens, time, session count) is exact and immediate.

### How the message counts split

The Overview tab shows two separate cards that summarize what got sent in your sessions:

- **Your Messages** — messages you typed yourself in the composer. Aside replies (the small follow-up questions you can fire alongside a session) count here too because you typed them. The sub-line shows the average per session. It deliberately does NOT count three things that land in the same place your messages do: a message another agent sent this one, a message another session or a scheduled job pushed in over the CLI, or a session's opening prompt (the Sessions card already counts those, and when a machine started the session that prompt is a machine's words). Before 2026-09-09 all three counted, so the card badly overstated you — on the dev box it read 10,700 against a real 4,645, and the daily briefing's "answered" chip showed 2,426 for a single day of which 73% was machinery. One shared definition now backs both surfaces ([/src/main/db/human-operator-turn-sql.ts](/src/main/db/human-operator-turn-sql.ts)), so the two screens can never disagree about who "you" is. **Avg Response Time still uses the older, looser test** and is still pulled toward zero by agent-to-agent turns.
- **Automated Actions** — combined count of two things Omniscio did on your behalf without you typing:
  - **In-session auto-replies**: messages sent into a Claude session by an **Away Mode** auto-reply rule (the "When the agent says X, reply with Y" rules under the Automations virtual project's **AUTO-REPLIES** category, sometimes called Quick-Reply rules). Counted from `conversation_messages` rows whose `metadata.autoRule` snapshot the engine attaches at write time; rows without that snapshot are excluded as ambiguous.
  - **Channel-driven Automations**: actions taken by your **Automations** rules outside the session conversation — SMS auto-replies, Slack/Telegram forwards, Gmail summaries, archive actions, label moves, notify pings, etc. Counted from `automation_runs` rows where the rule actually matched the message (`condition_matched = 1`), the action succeeded (`action_result = 'success'`), and the action was something other than the explicit no-op `pass` action (which exists so a chain can match without any side effect).
  - When the combined count is 0, the sub-line reads "No automated actions taken." Otherwise it shows the breakdown ("X in-session, Y channel-driven") so you can tell which side dominated.
- **System plumbing — counted in neither of these two cards.** When Omniscio nudges a stuck session (rate-limit recovery, "Please continue" auto-continue, auth retry, stall watchdog, silent-turn auto-retry, crash-recovery nudge, session-restart recovery), it sends a message under your identity but the message is plumbing, not a real reply. It's excluded from the two message-count cards above so they stay an honest answer to "how much did I write?" and "how much automation ran?". (A few of these — the "Please continue" auto-continue and the background-wait hold — DO count in the separate **Interruptions Avoided** card below, which measures a different thing: moments you weren't interrupted, not messages.)

Channel-driven actions are not broken down per-project on the **By Project** table because they live outside the session/project graph.

### Interruptions Avoided

The **Interruptions Avoided** card (Overview tab) is a running tally of the moments Omniscio handled something autonomously so you did **not** have to look, click, read, decide, or reply. It answers "how much is Omniscio saving me from being interrupted?" The headline number is the sum of three buckets, shown in the sub-line ("N self-archived, M auto-replied, K auto-approved"); when it's 0 the sub-line reads "Nothing handled automatically yet." Hovering the info (ⓘ) icon next to the card title shows a one-line plain-English explanation of each bucket.

- **Self-archived** — a session finished its work and archived **itself** off your board (via the `[[OMNISCIO_SELF_ARCHIVE]]` marker or the self-archive route). Counted only when the archive actually applied immediately — a self-archive that's queued for your approval (because you require approval for agent lifecycle actions) does **not** count, since you still had to act.
- **Auto-replied** — the app replied or held on your behalf instead of flipping the session to "Needs You" and pinging you: an **Away-Mode auto-reply** rule fired, an **auto-continue** nudged a mid-task agent onward, a re-woken session found nothing to do and quietly went back to sleep, or a session declared it was **waiting on a background process** and Omniscio kept it running.
- **Auto-approved** — a **dev-pipeline** gate advanced without you having to approve it.

It honors the **Time Range** selector, so "All Time" is your lifetime running tally and shorter ranges show a recent slice (attributed by each session's start time, like the other session metrics). Unlike the message counts above, this number is **exact and immediate** — it's a direct tally kept on each session, not the background-refreshed message count.

**Overlap with Automated Actions is intentional.** An Away-Mode auto-reply counts here **and** in the **Automated Actions** card — they answer different questions ("how many times was I _not_ interrupted?" vs. "how much automation ran?"), so the same underlying event legitimately appears in both.

**Turning it off.** Settings → System → **Show interruptions avoided** hides the card (default on). The counting itself always runs in the background (it's near-free), so turning the card back on shows the full tally, including any time it was hidden. Like the rest of Stats, it has no CLI route — it's a read-only renderer surface backed by the local database.

### What the By Project table shows

Each row is one project; rows are sorted by cost descending and capped by the same time-range filter as the cards above. A thin accent bar under the project name shows that project's cost relative to the top project. Four stats per row:

- **Cost** — total spend for sessions in this project, rendered in compact `$X.XK` form once it crosses $1,000 (e.g. `$1.2K`); below $1,000 it shows as raw integer dollars (e.g. `$87`).
- **Tokens** — combined input + output tokens (e.g. `1.5M`).
- **Active** — accumulated engaged seconds across this project's sessions (see "How Active Time is calculated"). Below 10 hours it shows as `Xh Ym`; at 10 hours and above it rounds to the nearest hour (e.g. `207h`).
- **LLM** — time the LLM was actually thinking, summed from `sessions.duration_api_ms` across this project's sessions. Hovering reveals a tooltip: "Time Claude spent thinking (sum of LLM API call durations)." This is the per-project counterpart to the top **LLM Time** tile. Same long-duration formatting as **Active** — `2,198h` at 1,000+ hours.

There is no longer a per-project "Your msgs/sess" cell — the cards above already summarize human vs. automated message counts globally, and the per-row column proved confusing without adding clear signal.

### How Active Time is calculated

"Active Time" is the only metric on the Overview tab that Omniscio infers rather than reads from the model — it's an estimate of how long you were actually focused on a session, not how long the session sat open.

The renderer samples engagement on a 5-second tick. A tick counts as engaged when **both** are true:

- The Omniscio window is focused (Electron `document.hasFocus()`, or for the mobile/web PWA the browser tab is visible).
- You produced an input event in the last 2 minutes — `mousemove`, `keydown`, `click`, `touchstart`, or `scroll`. `touchstart` is what makes mobile tap-and-long-press count even when no mouse fires; tap also generates a synthetic `click` and touch-scrolling fires `scroll`, so both desktop and mobile are covered without a separate code path.

Every 30 seconds the accumulated engaged seconds are flushed to the main process and added to the currently-viewed session's `engaged_seconds` column. Sessions are bucketed into the time range by their `started_at` timestamp — so engagement you spend today on a session that started yesterday lands in **Yesterday's** bucket on the Stats page, not Today's.

Failure recovery: if the heartbeat IPC drops (transient WebSocket loss for the mobile PWA, or the server rejects the envelope), the unflushed seconds are restored to the next heartbeat instead of being silently lost. The buffer is capped at 120 seconds so a long offline stretch can't grow it without bound; engagement beyond ~4 minutes of continuous failed flushes is dropped (preferable to retrying a payload the server will always reject).

What this means for the number you see:

- Time you spent reading without scrolling, mousing, or typing for >2 minutes drops out (idle threshold) — even though you may have been actively reading.
- Time on the inbox or other virtual projects (no attributable session) is not added to any session's `engaged_seconds` and so doesn't show up in Active Time at all.
- Time on a session in another window/app does not count — even if Omniscio is open, focus has to be on Omniscio.

If the number feels low, the most common causes are reading-without-input (idle dropout) and time spent on virtual-project views.

### How the Spend tab is computed

The Spend tab answers "where is my AI money going?" by merging two sources of cost that Omniscio already records. Two headline cards sit at the top: **All AI usage** (the grand API-equivalent total for the selected range) and **Out of pocket** (the subset actually billed to your own API keys — see "Usage Value vs money you actually paid" above). The sources:

- **Coding sessions** — the sum of `sessions.cost_usd` across every session (all engines combined into one line). This spend is **immortal**: every dollar you've ever spent counts, even on sessions you later deleted, sessions whose whole project you deleted, and sessions old enough that retention has permanently purged them (their cost is snapshotted into a tiny durable ledger just before deletion, so it's never lost). Deleting things never retroactively shrinks your spend history — it works like the background-feature ledger, which no delete can touch. The per-account cost views in Settings → Accounts count spend the same way, so the numbers stay reconcilable.
- **Background features** — per-feature spend from the `api_cost_log` cost ledger, where each row's `source` names the feature. Many internal source codes collapse to one feature name (every Plain Speak pipeline stage → "Plain Speak"; the routing + overlay decisions → "Inbox Pilot"; both title generators → "Session Titles"), so you see one row per feature, not per internal stage. A feature without a friendly name yet shows a tidied-up version of its raw code rather than disappearing.

**Counted once — no double-counting.** External engines (Codex, Pi, and the one-shot runner) record their cost in _two_ places: on the session _and_ in the cost ledger under the engine's name. The Spend tab drops those engine rows from the feature breakdown — their dollars are already in the "Coding sessions" line — so the grand total is honest. The total shown always equals the sum of the rows below it.

**Spend by engine.** Beneath the "Coding sessions" line, a per-engine drill-down splits that same session spend across the models you actually ran — Claude, Codex, Pi, Gemini, Cursor, and so on — biggest first. It always adds up to the "Coding sessions" total (it's that one number, partitioned by engine). Two honesty markers:

- **"≈ estimated"** — Codex reports how many tokens it used but not a dollar figure (it bills against a ChatGPT plan, not a metered API), so Omniscio prices those tokens at OpenAI's published rates. Gemini and Cursor are estimated the same way, priced at the chosen model's published rates. A close estimate, marked with a "≈".
- **"cost not reported"** — some engines (Antigravity, Hermes, OpenClaw) don't report cost _or_ token usage today, so Omniscio can't compute what they cost. Rather than a misleading **$0** (which reads as "free"), they're listed as "cost not reported by the tool" — they ran, the cost just isn't measurable yet. Engines that _do_ report real cost (Claude, Pi, OpenCode) show their exact dollar amount.

The same "≈" marker appears next to a single non-Claude session's own cost, so an estimated figure always reads as an estimate.

**Expand a feature row (drill-down).** Beside the per-engine split above, every _feature_ row (Email Summarizer, Session Titles, Plain Speak, Aside, …) expands to show the individual charges behind it: a per-model split, the charge count, and the biggest individual charges, biggest first. Each charge is resolved to a friendly label where AMC knows one — the **session** it belongs to (by name), a short **context label** (an email subject, a document name…), or, for Asides, the **question** you asked; charges AMC can't attribute show just the model and time. The list is capped at the biggest few (with an honest "Showing the N biggest of M charges." line when there are more) and it honors the Time Range selector like the rest of the tab. Attribution is **forward-only**: a charge carries its session/label only from the point that feature started recording it, so older charges drill down to model + time only. It all stays on your machine — the session id and label are local metadata, never sent anywhere.

### Privacy

Per-session metrics are stored locally. The "By Project" breakdown shows folder paths you've already exposed to Omniscio by registering the project. Nothing here is shared externally.

## For agents

### Data source

Stats reads from the local SQLite database — specifically `sessions`, `conversation_messages`, `usage_samples`, `feature_events`, the `inbox_item_events` inbox-attention-analytics log (for the Inbox tab), and (for the Spend tab) the `api_cost_log` cost ledger. No network calls. No data leaves your machine.

### CLI access

**The Stats views have no CLI control server route.** Every Stats surface — Overview, Spend, Feature Usage, Trends, Usage, and Records — is read-only and reachable only from the Electron renderer (the Stats virtual project in the Omniscio sidebar); none of its metrics are exposed over the CLI control server (`127.0.0.1:19519`). There are no `/stats/*` routes, so an external agent or script can't query spend, token usage, feature counts, trends, or records headlessly. Stats reads directly from the local SQLite database (see "Data source" above) — an agent that needs these numbers reads that DB read-only rather than calling a CLI endpoint.

## Related

- [Settings](settings-virtual-project.md) — for configuration (auto-replies, accounts, themes, etc.)
- [Feature events architecture](../../.claude/memory/feature-inventory.md) (repo-only) — how the `feature_events` table is populated
