Skip to content

Usage ​

The host reads what the CLIs cost from their transcripts, and how much of each plan is left from the CLIs themselves. It answers the usage.* requests of the contracts. AgentHost builds both services; this page covers what they read and how to build them yourself.

Summaries ​

UsageService reads the CLIs' transcripts, keeps an index in the data folder, and answers usage.summary for a period in the viewer's time zone. usage.subscribe keeps it scanning while a client follows. Every cost is an estimate, and a model without a known price costs null. Storage covers the files it keeps.

The scanner reads Grok's per-session usage.json under $GROK_HOME/sessions/ (default ~/.grok) without counting a turn twice. Grok's own cost (costUsdTicks) wins over the price table. Grok has no plan reader, so it has no limits.

Prices ​

Prices come from LiteLLM's price table at LITELLM_URL, or from the bundled snapshot @adecore/agents/usage/prices-snapshot.json when that cannot be fetched. Dollars convert to euros at the ECB reference rate from the Frankfurter API.

AgentHost fetches prices and rates when a page asks. A host that may not reach the network makes its own UsageService with allowPriceFetch: false and wires the handlers itself. The bundled table then prices everything.

Plan limits ​

UsageMonitor periodically reads each plan's remaining share from the CLIs and merges what running turns report. It answers usage.limits and sends usage.limitsChanged. A full read of a plan is a ProviderReading: the plan, its windows and the session cost. What a running turn reports in passing is a LimitsUpdate, a few windows of one CLI and account, which the core hands to applyLive.

For a provider with no CLI to ask, such as a subscription API the host calls with its own key, the host answers through plans with a ProbeResult, using fraction for each window's share. A plan function gets the LimitAccount it reads.

Transcripts of other providers ​

Every provider other than Claude Code, Codex and Grok needs the host to log its calls, which the scanner reads like a transcript. An ACP agent can write that log itself (see ACP agents). hostUsageLine writes one call (a HostUsageEntry) with the token kinds apart: input, cacheRead, cacheWrite, output, reasoning. Map your API's counts onto them, checking whether its input includes the cached part. Add a root for that folder, with its provider, to the roots of the UsageService.

A provider that runs Claude Code in a configuration folder of its own, such as one on an API key, needs no log. claudeTranscriptRoot(provider, configDir) is a root that reads Claude Code's transcripts there and counts them under provider. Its format: 'claude' tells the scanner how to read them.

A root is a UsageRootPath: a provider, a path, optionally the accounts whose CLI writes there, and the format. usageRoots(env) returns the roots of the default accounts, found through the variables the CLIs read.

Building the services yourself ​

AgentHost and wireAgents build both services from the accounts. A host with its own wiring builds them the same way:

ts
import { chatSessionAccounts, limitAccountsOf, UsageMonitor, UsageService, usageAccountsOf, usageRootsOf } from '@adecore/agents/usage';

const nameOf = (kind: string): string => providers.get(kind).name;

const limits = new UsageMonitor({ providers, accounts: limitAccountsOf(accounts, nameOf, env) });
accounts.listen(() => limits.accountsChanged());

const usage = new UsageService({
    home: dataDir,
    allowPriceFetch: false,
    knownProjects: async () => projects.map(({ id, name, folder }) => ({ projectId: id, name, folder })),
    roots: () => usageRootsOf(accounts),
    sessionAccounts: () => chatSessionAccounts(chats.list()),
    accounts: () => usageAccountsOf(accounts, nameOf)
});

providers is the ProviderRegistry, accounts the ProviderAccountsService and chats the ChatCore.

HelperWhat it gives
usageRootsOf(accounts)The transcript folders of every account the machine has.
usageAccountsOf(accounts, nameOf)Every account the usage page may filter by, on or off, since what it spent still counts.
limitAccountsOf(accounts, nameOf, env)The LimitAccounts the monitor reads: the default account of every CLI and every other one that is on and signed in. Each is a LimitAccount, and a probe runs in its ProbeEnv.
chatSessionAccounts(chats)The account each CLI session of a chat ran under, for transcript folders that accounts share.

knownProjects returns KnownProject records, so the usage page shows a folder under your project's name. UsageServiceOptions, UsageMonitorOptions and UsageScannerOptions list every option. Hand both services to usageHandlers(usage, limits) from @adecore/agents/host to answer the requests.

Licenses and third-party notices are listed in each package.