Docs for SCORPIOX CODE @ 77c49df

Token Usage Observability

Every response reports tokens in, out, cache reads/writes, rate limits and speed - visible live in the status bar, mirrored to stats.json once a second, pushed to embedding hosts every two seconds, and off by default for anything that leaves your machine.

Every API response SCORPIOX CODE receives carries a token-usage report: how many tokens went in, how many came out, how much of your prompt was served from the provider's cache, how close you are to your rate limits, and how fast tokens are flowing. SCORPIOX CODE makes that report visible in three places — the live status bar in the terminal interface, a machine-readable stats.json file in the session folder, and a status event pushed to host applications that embed the agent. On top of those local surfaces, an opt-in tracker can post usage counts to a usage API.

All three local surfaces are computed from the same data, so they always agree — and none of them sends anything anywhere.

Docs for SCORPIOX CODE @ 77c49df.

The whole idea in one line: the status bar shows the last three turns live, .scorpiox/sessions/<session>/stats.json mirrors the same telemetry as a JSON file refreshed about once a second for your own tooling, and embedded hosts receive the same numbers as a status event every two seconds — while anything that leaves your machine is off unless you turn it on.


The surfaces at a glance

SurfaceWhere it livesRefreshAudience
Status barBottom of the terminal UIEvery frameYou, while you work
stats.json.scorpiox/sessions/<session>/stats.json~1 Hz + on every responseScripts, dashboards, watch
Embedding status eventDelivered to the host app (callback or event queue)Every 2 sHost applications (WPF, bots, SDK consumers)
Usage tracking (opt-in)HTTPS POST to USAGE_API_URLAfter every API responseA usage collection endpoint you choose

The first three are local-only. The fourth is the one thing that can cross the network, and it ships disabled.


The live status bar

The bottom of the terminal interface stacks the last three responses, newest at the bottom of the block and drawn at full brightness; the two older rows are dimmed (70% and 50%) so your eye lands on the current turn. A typical row reads:

rsn:412 T:1847 in:212 R:98341 W:1204 out:1635 5h:12% 7d:3% ~pp:2143t/s ~tg:87t/s

Per-turn fields

FieldMeaningShown when
rsn:Reasoning tokens the model spent thinkingProvider reports them (> 0)
T:Total for this API call — input + output tokensAlways
in:Input tokens processed fresh this turnAlways
R:Cache-read tokens (prompt served from cache — green, the good stuff)> 0
W:Cache-write tokens (prompt prefix newly written to cache — orange)> 0
out:Output tokens generatedAlways
5h:5-hour rate-limit utilization, percentProvider reports it
7d:7-day rate-limit utilization, percentProvider reports it
pp:Prompt-processing speed, tokens/secondServer reports timings, or estimate enabled
tg:Token-generation speed, tokens/secondServer reports timings, or estimate enabled

Two refinements on the speed fields: a tilde (~pp:, ~tg:) means the number is a client-side estimate, not a server-reported timing; and ~e2e: instead of ~tg: means only an end-to-end rate was measurable (no first-token timestamp), so generation speed is approximated as output over total wall time. Server-reported timings always win over estimates. Estimates are enabled with USAGE_SPEED_ESTIMATE=1 (off by default), and on OpenAI-compatible endpoints they additionally need OPENAI_STREAM=1 so the client can see the first token arrive.

The rate-limit fields are color-coded by utilization: green below 50%, yellow from 50–80%, red above 80%.

Around the usage rows, the status bar carries the session chrome:

ElementMeaning
folder icon + pathCurrent working directory (left, clickable — opens the file explorer). Home is abbreviated to ~
branch icon + nameCurrent git branch (hidden outside a repo)
CTX:190KThe configured context-window threshold — the point where SCORPIOX CODE compacts the conversation (default 190,000 tokens; shown as CTX:190K)
T 4:32Cache countdown — time left before the provider's prompt cache expires (see below)
KA indicatorCache keep-alive state: KA:<n> green when pings are keeping the cache warm, KA* while a ping is in flight, KA|| when paused after misses, KA- idle, hidden when keep-alive is off
[CB:n 2:14]Scheduled callbacks: how many are active and time until the next one fires
No token data yetPlaceholder until the first response arrives

The cache timer

The T m:ss countdown tracks how long your prompt cache stays hot. The total window follows the keep-alive trigger: with keep-alive disabled it is the provider's default 5 minutes (green while more than 60% remains, yellow under 3 minutes, red under 1 minute, and T --:-- once expired). When keep-alive is enabled, the window matches CACHE_KEEPALIVE_TRIGGER (default 270 s), so the timer doubles as a preview of when the next keep-alive ping will fire. Every real response — and every successful keep-alive ping — resets the clock.


stats.json — the machine-readable contract

Everything the status bar knows is also written to a JSON file in the session folder, refreshed at most once per second while the interface runs and immediately after each response lands. This is the surface for your own tooling: point watch, a Grafana JSON datasource, a CI script, or a sidebar widget at it and you get the same numbers the UI shows.

.scorpiox/sessions//stats.json

The session id looks like 2026_10_03_prickly_liskov. The file is written atomically — the content lands in a temporary file that replaces the old one in one step — so a reader never observes a half-written file, even at 1 Hz.

Structure

{
  "updated_ms": 1759420692000,
  "model": "sonnet",
  "cwd": "~/projects/demo",
  "branch": "main",
  "context_max": 190000,
  "history": [
    {"T":1847,"in":212,"R":98341,"W":1204,"rsn":412,"out":1635,
     "rl_5h":0.1200,"rl_7d":0.0340,"pp":2143.10,"tg":87.40,"speed_estimated":1},
    {"T":980,"in":180,"R":97120,"W":0,"rsn":0,"out":800,
     "rl_5h":0.1100,"rl_7d":0.0330,"pp":0.00,"tg":0.00,"speed_estimated":0}
  ],
  "cache_timer": {
    "last_response_ms": 1759420678000,
    "elapsed_s": 14,
    "remaining_s": 286,
    "fresh_s": 180,
    "total_s": 300,
    "color": "green"
  },
  "keepalive": { "state": "DISABLED", "ping_count": 0 },
  "callbacks": { "active_count": 0, "next_fire_ms": 0, "remaining_s": 0 },
  "bash": { "running": false, "command": "" },
  "bgtasks": { "running": 0, "total": 0 },
  "retry": {
    "active": false, "http_code": 0, "attempt": 0, "max_attempts": 10,
    "next_at_ms": 0, "remaining_s": 0, "reason": ""
  },
  "usage": {
    "input_tokens": 212,
    "output_tokens": 1635,
    "cache_read_tokens": 98341,
    "cache_creation_tokens": 1204,
    "reasoning_tokens": 412,
    "total_tokens": 1847,
    "rl_5h": 0.1200,
    "rl_7d": 0.0340,
    "prompt_tps": 2143.10,
    "gen_tps": 87.40,
    "speed_estimated": 1
  }
}

Field reference

Top level

KeyTypeMeaning
updated_msintEpoch milliseconds when this snapshot was written — check it to detect a stale file
model, cwd, branchstringCurrent model name, working directory, git branch (empty when outside a repo)
context_maxintContext-window threshold in tokens (the CTX: indicator's raw value)
historyarrayUp to 3 usage entries, newest first — same fields as the status bar rows (see below)
usageobjectThe latest response's usage, exploded into full names (this is the convenient one for scripts)

history entries (and the equivalent usage block, which uses its long-form key names)

KeyMeaning
TTotal for the call: input + output tokens
in / outInput and output tokens
R / WCache-read and cache-write tokens
rsnReasoning tokens
rl_5h / rl_7dRate-limit utilization as a fraction (0.12 = 12%); 0 when the provider doesn't report it
pp / tg (history), prompt_tps / gen_tps (usage block)Prompt-processing and generation speed, tokens/second; 0 when unknown
speed_estimated0 = server-reported timings, 1 = client-side pp/tg estimate, 2 = end-to-end estimate only

Note the two speed-key spellings: compact pp/tg inside history, long-form prompt_tps/gen_tps inside usage.

cache_timer

KeyMeaning
last_response_msEpoch ms of the newest response (what the countdown counts from)
elapsed_s / remaining_sSeconds since it, seconds left (capped at the window and at 99:59)
fresh_s / total_sGreen threshold and full window in seconds — follows CACHE_KEEPALIVE_TRIGGER when keep-alive is on (default 300/180 when off)
colorgreen, yellow, red, or expired — the same color the UI timer uses

Status blocks

KeyMeaning
keepalivestate: DISABLED, IDLE, ACTIVE, PINGING, or PAUSED; ping_count: pings sent for the current session
callbacksactive_count, next_fire_ms (epoch, 0 = none), remaining_s until the next fire
bashMinimized background shell: running + the command it is running
bgtasksBackground task counters: running and total
retryTransient-error retry state: active, upstream http_code, attempt/max_attempts, next_at_ms, remaining_s, and a short reason

Reading it

# Live view, once per second
watch -n 1 cat .scorpiox/sessions/$(ls -t .scorpiox/sessions | head -1)/stats.json

# Pull the latest cache-read count with jq
jq '.usage.cache_read_tokens' .scorpiox/sessions/*/stats.json

If you work inside a git worktree, the file is dual-written: once under the worktree's .scorpiox/sessions/<id>/ and once under the main repository's, so a dashboard watching the main repo still sees worktree sessions.


The embedding / SDK path

Host applications that embed the agent engine (a WPF shell, a bot process, any consumer of the sx.dll P/Invoke interface) receive the same telemetry as a status event — a JSON string pushed to the host every 2 seconds, either through a registered event callback (instant delivery) or by polling the event queue.

{
  "model": "sonnet", "provider": "anthropic", "session": "2026_10_03_prickly_liskov",
  "tools": true, "thinking": true, "busy": false,
  "mem_mb": 412.3, "cpu_pct": 7.1, "forks": 0, "fds": 34,
  "cwd": "C:\\projects\\demo", "branch": "main",
  "in": 212, "out": 1635, "cache_r": 98341, "cache_w": 1204, "rsn": 412,
  "total": 101392, "ctx": 190000,
  "cb_active": 0, "cb_next_sec": -1, "cache_sec": 286,
  "rl_5h": 0.1200, "rl_7d": 0.0340,
  "pp": 2143.10, "tg": 87.40, "speed_estimated": 1
}
FieldMeaning
model, provider, sessionActive model, provider name, session id
tools, thinking, busyTool use enabled, thinking enabled, agent currently processing
mem_mb, cpu_pct, forks, fdsHost process resource snapshot
cwd, branchWorking directory and git branch
in, out, cache_r, cache_w, rsnThe latest response's usage breakdown
totalInput + output + both cache fields — see the gotcha below before comparing this to T
ctxContext-window threshold (the CTX: value)
cb_active, cb_next_secActive callback count; seconds until the next fire (-1 = none)
cache_secSeconds of cache lifetime remaining on a fixed 5-minute window (-1 = no response yet)
rl_5h, rl_7dRate-limit utilization fractions
pp, tg, speed_estimatedSpeeds and their provenance, same semantics as stats.json

Sessions run headless or with SDK consumers can additionally emit per-turn usage files: with --emit-session (implied by --headless), each response writes messages/msg_NNNN_usage.json inside the session folder, containing turn, in, out, cache_read, cache_create, plus the full rate-limit set (rl_5h, rl_7d, rl_overage, rl_status, rl_claim). Unlike stats.json, these files are an append-only per-turn record — every turn, forever, not just the last three.


Opt-in usage tracking

By default, nothing described so far leaves your machine. The one outbound surface is the usage tracker, and it ships disabled:

# scorpiox-env.txt
USAGE_TRACKING=0                      # default: off
USAGE_API_URL=https://code.scorpiox.net/usage-send

With USAGE_TRACKING=1, after every API response SCORPIOX CODE fires a one-shot scorpiox-usage send command (fire-and-forget, never blocks your turn) that POSTs a small JSON record to the configured endpoint. The record contains:

{
  "metadata": {
    "session_id": "2026_10_03_prickly_liskov",
    "provider": "anthropic",
    "model": "sonnet",
    "hostname": "workstation-1",
    "username": "alice",
    "os": "linux", "arch": "x86_64", "os_version": "6.8.0",
    "project": "demo", "branch": "main",
    "scorpiox_version": "1.2.3",
    "ratelimit_5h_utilization": 0.1200,
    "ratelimit_7d_utilization": 0.0340,
    "ratelimit_overage_utilization": 0.0000
  },
  "usage": {
    "input_tokens": 212,
    "output_tokens": 1635,
    "cache_creation_input_tokens": 1204,
    "cache_read_input_tokens": 98341
  }
}

Notes on the payload:

This is the same zero-collection stance as everything else in SCORPIOX CODE: see Privacy Architecture and Zero Data Collection for what stays on disk and what never leaves your machine, and API Traffic Logging and Complete Remote Call Transparency for the verbatim on-disk capture of every request.


Gotchas