gptme-sessions
Session tracking and analytics for agents. Supports trajectories from gptme, Claude Code, Codex, Copilot, Grok Build, and Pi native v3 sessions.
Provides an append-only JSONL-based session record system that any agent can use to track operational metadata across sessions: which harness and inference provider ran, what model was used, what type of work was done, and the outcome.
Installation
pip install gptme-sessions
Usage
Python API
from pathlib import Path
from gptme_sessions import SessionRecord, SessionStore
# Create a store (defaults to ./state/sessions/)
store = SessionStore(sessions_dir=Path("state/sessions"))
# Append a session record
store.append(SessionRecord(
harness="pi",
provider="openai-codex",
model="gpt-5.6-luna",
run_type="autonomous",
category="code",
outcome="productive",
stop_reason="stop",
cost_usd=0.0004264,
duration_seconds=2400,
deliverables=["abc123"],
))
# Query records
recent = store.query(model="gpt-5.6-luna", since_days=7)
# Get stats
stats = store.stats()
print(f"Success rate: {stats['success_rate']:.0%}")
Grok Build usage records retain sys_prompt_tokens (the first observed prompt,
including cached input) and context_peak_tokens (the largest per-call prompt).
These are stored alongside session input, output, cache-read, cache-creation,
and total token counts, so analytics do not need to reparse the trajectory.
The terminal end.usage provides cumulative totals, never context size. An
incomplete stream uses its observed per-call totals; older streams with only
an end record leave context metrics unknown.
CLI
# Show stats (default — auto-falls back to discover if store is empty)
gptme-sessions stats
# Show details for a single session by ID (or prefix)
gptme-sessions show a1b2c3d4
gptme-sessions show a1b2 --json
# Query with filters
gptme-sessions query --model opus --since 7d
gptme-sessions query --run-type autonomous --outcome productive --json
# Structured export (JSON or CSV) — backups, audit trails, data portability
gptme-sessions export --format json --since 7d
gptme-sessions export --format csv --category code --model opus -o sessions.csv
# --since accepts sub-day windows and natural phrasing (units: s, m, h, d, w).
# Sub-day windows filter precisely (no rounding up to a whole day).
gptme-sessions query --since 2h # last 2 hours
gptme-sessions query --since 30m --stats # last 30 minutes
gptme-sessions query --since "2 hours ago" # same as 2h
gptme-sessions query --since all # no time filter
# Run analytics (duration distribution, NOOP rates, trends)
gptme-sessions runs --since 14d
# Discover trajectory files across all harnesses (no store required)
gptme-sessions discover --since 7d
gptme-sessions discover --harness claude-code --signals
gptme-sessions discover --harness pi --signals
# Import discovered sessions into the store (safe to re-run — deduplicates)
gptme-sessions sync --since 14d
gptme-sessions sync --signals # extract productivity signals (slower)
gptme-sessions sync --dry-run # preview what would be imported
# Annotate an existing session record (amend fields after the fact)
gptme-sessions annotate a1b2c3d4 --outcome productive --add-deliverable pr#42
gptme-sessions annotate a1b2 --duration 3600 --token-count 50000
# Score recent sessions with an LLM judge (goal-alignment rating 1–5)
gptme-sessions judge
gptme-sessions judge --last 5
gptme-sessions judge --update-store # write scores back to the store
# Record a session at the end of an agent run (full pipeline)
gptme-sessions post-session --harness gptme --model opus \
--trajectory ~/.local/share/gptme/logs/2026-03-07-foo/conversation.jsonl
# Reasoning telemetry: --reasoning-profile is the semantic intent
# (routine|default|deep); --reasoning-effort is the backend-native level the
# harness ran with (low/medium/high/xhigh/max/ultra/...; free-form, unknown
# values warn but never fail). Omit --reasoning-effort to fill it from the
# trajectory (Claude Code `effort`, Codex `reasoning_effort`, gptme metadata).
gptme-sessions post-session --harness claude-code --model claude-fable-5-1 \
--reasoning-profile deep --reasoning-effort high --trajectory ~/.claude/projects/x/id.jsonl
# Append a record manually (deprecated: prefer post-session or sync)
gptme-sessions append --harness claude-code --model opus --outcome productive
# Custom sessions directory
gptme-sessions --sessions-dir /path/to/state/sessions stats
Pi discovery recursively scans native v3 tree sessions under
PI_CODING_AGENT_SESSION_DIR, or $PI_CODING_AGENT_DIR/sessions and then
~/.pi/agent/sessions when no direct override is set. Print-mode streams and
unsupported native versions are visibly warned and skipped instead of being
imported as false NOOPs. For a session Pi is actively appending, discovery uses
the last complete newline-delimited prefix; a transient partial tail cannot hide
that session or abort discovery of its siblings.
Discovery and sync are non-mutating. A synced record retains the source
trajectory_path; it does not copy or own the trajectory. Pi session JSONL is
a historical artifact, so keep or independently back up the source tree—do not
delete it after sync.
The parser is pinned to Pi 0.84.4's v3 session contract and route catalogs. Run the explicit upstream drift sentinel after upgrading Pi or refreshing retained fixtures:
cd packages/gptme-sessions
uv run python3 scripts/check_pi_compat.py
The command downloads Pi's release source archive and fails if the session
version, entry types, stop reasons, pinned route catalogs (Anthropic, OpenAI
Codex, xAI), or live fixture models no longer match. Two retained upstream
fixtures use models already retired from those catalogs
(google-antigravity/claude-opus-4-5-thinking and openai-codex/gpt-5.3-codex);
they remain parser-shape coverage only. Network/download failures are errors
rather than false green checks. Update the parser and retained fixtures before
advancing the pin.
Session provenance (blame)
gptme-sessions blame answers "which AI session produced this line / commit?"
by correlating git author-dates with session time-windows from the records store.
# Attribute the commits touching a file to their authoring session(s)
gptme-sessions blame scripts/watchdog.py
gptme-sessions blame scripts/watchdog.py --line 42
gptme-sessions blame scripts/watchdog.py --limit 5 --json
# GitHub refs (PR or issue) — no local git history needed
# (requires the gh CLI installed and authenticated)
gptme-sessions blame gptme/gptme-contrib#1252
# Point at a specific records store (default: auto-detected from git root)
gptme-sessions blame src/hello.py --records /path/to/session-records.jsonl
A runnable, self-contained demo lives in
examples/sessions-blame/ — it builds a
throwaway repo, makes a commit inside a sample session window, and shows the
attribution (./demo.sh).
Model Normalization
Model names are automatically normalized to short canonical forms:
| Input | Normalized |
|---|---|
claude-opus-4-6 |
opus |
anthropic/claude-sonnet-4-5 |
sonnet |
openrouter/anthropic/claude-haiku-4-5 |
haiku |
gpt-5.3-codex |
gpt-5.3-codex |
Storage Format
Records are stored as append-only JSONL (one JSON object per line):
{"session_id":"a1b2c3d4","timestamp":"2026-08-31T12:00:00+00:00","harness":"pi","provider":"openai-codex","model":"gpt-5.6-luna","run_type":"autonomous","category":"code","outcome":"productive","stop_reason":"stop","cost_usd":0.0004264,"duration_seconds":2400,"deliverables":["abc123"]}
cost_usd is the USD-equivalent cost reported by the harness. With OAuth or
subscription access it can be a nominal API-equivalent value rather than an
incremental charge on the subscription invoice. Missing cost is null; a
reported 0.0 is retained as a real observation.
Extending
Agent-specific features (journal parsing, log extraction, backfill) should be built on top of this package by importing SessionRecord and SessionStore.
Development
cd packages/gptme-sessions
uv run pytest tests/ -v