Skip to content

UsagePage ​

What the agent CLIs of a host cost over a period, read from their transcripts by the host: a total, a chart per day or hour, tiles for the kinds of tokens, a breakdown by model, folder and day, and what is left of each plan. The page asks the host; it scans nothing itself.

tsx
import { UsageDialog } from '@adecore/agents-react/usage/UsageDialog';
import { UsagePage } from '@adecore/agents-react/usage/UsagePage';
tsx
import { useState } from 'react';
import { Button, ErrorBoundary } from '@adecore/ui';
import { UsageDialog } from '@adecore/agents-react/usage/UsageDialog';
import { UsageLimitsCard } from '@adecore/agents-react/usage/UsageLimitsCard';
import { UsagePage } from '@adecore/agents-react/usage/UsagePage';
import { AgentDemo } from '../shared/agents.tsx';

export default function UsagePageDemo() {
    const [open, setOpen] = useState(false);

    return (
        <AgentDemo>
            <div className="flex items-center gap-2">
                <Button variant="primary" onClick={() => setOpen(true)}>
                    Open usage
                </Button>
                <UsageLimitsCard>
                    <Button variant="secondary">Plan limits</Button>
                </UsageLimitsCard>
            </div>
            <UsageDialog open={open} onOpenChange={setOpen}>
                <ErrorBoundary label="Usage">
                    <UsagePage />
                </ErrorBoundary>
            </UsageDialog>
        </AgentDemo>
    );
}
tsx
<UsageDialog open={open} onOpenChange={setOpen}>
    <ErrorBoundary label="Usage">
        <UsagePage key={scope.id} />
    </ErrorBoundary>
</UsageDialog>

UsageDialog is the frame, a dialog larger than the settings; what is in it mounts only while it is open, so nothing is asked behind a closed dialog. UsagePage is its body and reads the host of the scope it is rendered in. Give it a key per host: an account picked on one host means nothing on another.

What it asks ​

While it is mounted, the page sends usage.subscribe, which keeps the host scanning, asks usage.summary for the period on screen, and asks again on every usage.changed. It sends usage.unsubscribe when it goes. A reply to an earlier question than the one on screen is never drawn. A request that fails keeps the numbers it had and says so under them. Without a link to the host it shows what it last had, or an empty state when it never had anything.

ChoiceValuesKept
Periodtoday (per hour), 7d, 30d, 90d (per day)Yes, 7d
Metriccost, tokensYes, cost
CurrencyUSD, EUR at the rate the summary carriesYes, USD
AccountAll, or one account once a CLI has severalNo

The period runs in the viewer's own calendar days and time zone, which travel with the request. windowFor(period) and summaryPayload(period, account) make that request.

Props ​

PropType
pickersReactNodeControls of your own, first in the header, such as a picker of the host.
notice(stale: boolean) => ReactNodeA line over the numbers about the host itself; stale is true while older numbers stand under it.

UsagePane ​

The section of the settings dialog for usage: the currency, and a button to the page. onOpenPage is how your app lets the settings make way for the usage dialog.

tsx
import { useState } from 'react';
import { UsagePane } from '@adecore/agents-react/usage/UsagePane';
import { AgentDemo } from '../shared/agents.tsx';

export default function UsagePaneDemo() {
    const [asked, setAsked] = useState(0);

    return (
        <AgentDemo>
            <div className="flex w-full max-w-xl flex-col gap-2">
                <UsagePane onOpenPage={() => setAsked((count) => count + 1)} />
                <p className="text-xs text-text-muted">
                    {asked === 0 ? 'The app opens the usage page from here.' : `Asked to open the usage page ${asked} times.`}
                </p>
            </div>
        </AgentDemo>
    );
}
tsx
<UsagePane onOpenPage={() => openUsage()} />

State ​

useUsageStore keeps the three choices, kept in storage, and per scope the summary, the question it answers, whether it loads or failed, and the plan limits. useUsage(select) reads it for the scope in context. askedKey(payload) is the key a summary is kept under.

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