session_stats
Summarize and rank coding agent sessions by agent, project, date, type, or model for a birds-eye audit view. Get session counts, edits, intents, and token usage in one call.
Instructions
Summarise sessions, grouped and ranked — the bird's-eye audit view.
Where find_file_edits / find_tool_calls return flat record
streams, this rolls the sessions themselves up by one dimension so you
can see how the work is distributed in a single call.
group_by is one of:
"agent"(default) — claude vs codex vs opencode vs ..."dir"— by working directory / project (the normalizedproject_dirfirst — one real directory = one bucket across agents — thencwdfor codex/pi / project slug for claude;"(unknown)"for agents without any signal)."date"— by calendar day (YYYY-MM-DD)."kind"— top-level agent sessions vs spawned subagent sessions."model"— by the model that produced the session. A session that mixed models buckets as"(mixed)"rather than being attributed to one of them; one whose transcript records no model is"(unknown)". Neither is guessed. Pair withwith_tokens=Trueto see what each model actually cost.
Each group carries its session count plus enrichment from the shared
find_file_edits core: edits (file edits attributed to the group's
sessions), intents (distinct requests behind those edits), the
distinct agents in the group, and total messages.
RISK-4 note: subagent detection is currently Claude-only. When no
subagent sessions are in scope, a group_by="kind" result shows a
single agent bucket — so the result always carries
kind_split_available (False here) plus a note making clear
that this is NOT a verified "no subagents", just an absent split.
with_tokens=True (F3.3) additionally reads every matched session's
token usage at request time (nothing runs in the background) and
adds a folded tokens block to each group and to totals:
{input, output, reasoning, cache_read, cache_write, total, exact, estimated, unknown}. Per session the numbers are exact where the
agent's own files record usage (Claude message.usage, Codex
token_count, OpenCode message.data.tokens, Pi usage); a
session without a recorded signal (e.g. Antigravity) gets a
transcript-volume estimate — tokenized with the optional
tiktoken dependency (pip install "ai-r[tokens]") when installed,
else a rough chars/4 heuristic — and counts under estimated, never
silently mixed in as exact; no signal at all counts under unknown.
Sums that no session carried stay null (never a fabricated 0).
The block contains only ai-r-computed integers and labels — no raw
session text — so it is outside the redaction surface by construction.
Default False: byte-identical historical output, no extra reads.
Scan guard (token_scan_limit): because with_tokens reads every
matched session's files at request time, an unscoped run over a huge
corpus is a multi-hour I/O storm. When with_tokens is set with no
narrowing filter (agent/since/until) and more than
token_scan_limit sessions match, the call returns
{"error": "scope_required", ...} (naming the count and the limit)
INSTEAD of scanning — the check runs on the cheap inventory count before
any file is read. Narrow the scope, or raise token_scan_limit (0
disables the cap) to force the full scan. A permitted-but-large scan runs
but carries a warning.
Thin wrapper over :func:ai_r.session_stats.session_stats that
translates the core ValueError contract into the
{"error": "invalid_argument", "message": str(exc)} shape the MCP
client expects.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| top | No | ||
| agent | No | ||
| since | No | ||
| until | No | ||
| group_by | No | agent | |
| edit_path | No | / | |
| with_tokens | No | ||
| token_scan_limit | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||