Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled

Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11),
free to diverge. Tree copied sans upstream .git; upstream remote added for
future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19.
Preserves docs/ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
2026-07-05 09:36:54 +00:00
parent 3216769225
commit bedb527145
21108 changed files with 6010766 additions and 0 deletions

View File

@@ -0,0 +1,328 @@
---
summary: "Usage tracking surfaces and credential requirements"
read_when:
- You are wiring provider usage/quota surfaces
- You need to explain usage tracking behavior or auth requirements
title: "Usage tracking"
---
## What it is
- Pulls provider usage/quota directly from each provider's usage endpoint. No estimated costs; only provider-reported quota windows, balances, or account-state summaries.
- Human-readable quota-window output is normalized to `X% left`, even when a provider reports consumed quota, remaining quota, or only raw counts. Providers without resettable quota windows show provider summary text instead (for example a balance).
- Session-level `/status` and the `session_status` tool fall back to the session's transcript log when the live session snapshot is missing token/model data. That fallback fills missing token/cache counters, can recover the active runtime model label, and prefers the larger prompt-oriented total when session metadata is missing or smaller (`totalTokensFresh !== true`, zero, or below the transcript-derived value). Nonzero live values always win over the fallback.
## Where it shows up
- `/status` in chats: status card with session tokens and estimated cost (API key models only). Provider usage shows for the **current model provider** when available, as a normalized `X% left` window or provider summary text.
- `/usage off|tokens|full` in chats: per-response usage footer.
- `/usage cost` in chats: local cost summary aggregated from OpenClaw session logs.
- CLI: `openclaw status --usage` prints a full per-provider usage/quota breakdown.
- CLI: `openclaw models status` lists OAuth/token auth profiles and shows a usage-window summary next to each provider that has one.
- macOS menu bar: a root "Usage" section appears below Context when provider usage snapshots are available. See [Menu bar](/platforms/mac/menu-bar).
`openclaw channels list` no longer prints provider usage; it points users to `openclaw status` or `openclaw models list` instead.
## Default usage footer mode
`/usage off|tokens|full` sets the footer for a session and is remembered for that
session. `messages.responseUsage` seeds that mode for sessions that have not
chosen one, so the footer can be on by default without typing `/usage` each time.
Set one mode for every channel, or a per-channel map with a `default` fallback:
```jsonc
{
"messages": {
"responseUsage": "tokens",
// or: { "default": "off", "discord": "full" }
},
}
```
Accepted values: `"off"`, `"tokens"`, `"full"`, and the legacy alias `"on"` (treated as `"tokens"`).
### Three distinct session states
A session's `responseUsage` field has three representable states, each with
different semantics:
| State | Stored value | Effective mode |
| ------------------- | ------------------------------- | --------------------------------------------------------------------- |
| **Unset / inherit** | `undefined` (absent) | Falls through to `messages.responseUsage` config default, then `off`. |
| **Explicit off** | `"off"` (stored) | Always off, a non-off config default cannot re-enable the footer. |
| **Explicit on** | `"tokens"` or `"full"` (stored) | That mode, regardless of config default. |
### Precedence
Effective mode = session override → channel config entry → `default``off`.
An explicit `/usage off` is **persisted** as the literal value `"off"` in the
session, not the same as "unset." A non-off `messages.responseUsage`
default cannot turn the footer back on once the user has explicitly disabled it.
### Resetting vs. turning off
- `/usage off` forces the footer off and persists that choice. A configured
non-off default cannot override this.
- `/usage reset` (aliases: `default`, `inherit`, `inherited`, `clear`, `unpin`) clears the session
override. The session then **inherits** the effective config default
(`messages.responseUsage`). If no default is configured, the footer stays off.
- A full session reset (`/reset` or `/new`) or a session rollover **preserves**
the explicit usage-mode preference so the user's display choice survives
session rollovers. Only `/usage reset` (and its aliases) clears the override.
### Toggle behavior
`/usage` with no arguments cycles: off → tokens → full → off. The starting point
for the cycle is the **effective** current mode (session override falling through
to the config default when unset), so the cycle always matches what
the user currently sees in the footer.
### Config
With no config the prior behavior holds (footer off until `/usage`). Use
`/usage reset` to clear a session override and re-inherit the configured default.
## Custom `/usage full` footer
`/usage tokens` always renders a plain `Usage: X in / Y out` line (plus cache and
estimated-cost suffixes when available). Only `/usage full` renders the richer
footer described below.
`/usage full` shows a built-in compact footer with model, reasoning, fast/slow,
context window, and cost when those fields are available. No template file is
required for the built-in footer.
`messages.usageTemplate` is only for advanced custom layouts. The value is a
JSON file path (supports `~`) or an inline object, and it replaces the built-in
footer when valid. A file path is watched and reloaded live on change.
```json
{
"messages": {
"usageTemplate": "~/.openclaw/usage-footer.json"
}
}
```
Missing or empty templates fall back to the built-in footer quietly. Unreadable
or invalid configured templates (bad JSON, or a shape with no renderable output
pieces) also fall back to the built-in footer and emit an operator warning.
Start custom templates from the built-in shape, then edit the parts you want to
change:
```jsonc
{
"schema": "openclaw.usageBar.v1",
"scales": {
"braille": "⠐⡀⡄⡆⡇⣇⣧⣷⣿",
"block": "░▏▎▍▌▋▊▉█",
"shade": "░▒▓█",
"moon": "🌑🌘🌗🌖🌕",
"level": "▁▂▃▄▅▆▇█",
"weather": ["🥶", "☁️", "🌥", "⛅️", "🌤", "☀️"],
"plants": ["🪾", "🍂", "🌱", "☘️", "🍀", "🌿"],
"moons6": ["🌑", "🌚", "🌘", "🌗", "🌖", "🌝"],
},
"aliases": {
"models": {
"claude-opus-4-6": "opus46",
"claude-opus-4-8": "opus48",
"claude-sonnet-4-6": "sonnet46",
"claude-haiku-4-5": "haiku45",
"gpt-5.5": "gpt5.5",
},
"reasoning": {
"off": "🌑",
"minimal": "🌚",
"low": "🌘",
"medium": "🌗",
"high": "🌕",
"xhigh": "🌝",
},
},
"output": {
"sep": "",
"default": [
{ "text": "{model.provider}{identity.emoji|🤖}{model.display_name|alias:models}" },
{ "map": "model.is_fallback", "cases": { "true": "🔄" } },
{ "map": "model.is_override", "cases": { "true": "📌" } },
{ "when": "model.reasoning", "text": "{model.reasoning|alias:reasoning}" },
{ "map": "state.fast_mode", "cases": { "true": "⚡️", "false": "🐌" } },
{
"when": "context.max_tokens",
"text": " | 📚[{context.pct_used|meter:5:braille}]{context.max_tokens|num}",
},
{ "when": "cost.turn_usd", "text": " 💰{cost.turn_usd|fixed:4}" },
],
"surfaces": {
"discord": [
{ "text": "-# -\n" },
{ "text": "-# {model.provider}{identity.emoji|🤖}{model.display_name|alias:models}" },
{ "map": "model.is_fallback", "cases": { "true": "🔄" } },
{ "map": "model.is_override", "cases": { "true": "📌" } },
{ "when": "model.reasoning", "text": "{model.reasoning|alias:reasoning}" },
{ "map": "state.fast_mode", "cases": { "true": "⚡️", "false": "🐌" } },
{
"when": "context.max_tokens",
"text": " | 📚[{context.pct_used|meter:5:braille}]{context.max_tokens|num}",
},
{ "when": "cost.turn_usd", "text": " 💰{cost.turn_usd|fixed:4}" },
],
},
},
}
```
### Shape
```jsonc
{
"schema": "openclaw.usageBar.v1",
"scales": { "<name>": "low-to-high glyphs" }, // string (1 glyph/char) or array
"aliases": { "<table>": { "<value>": "<label>" } },
"output": {
"sep": "", // joins surviving pieces
"default": [
/* pieces */
], // fallback for any surface
"surfaces": {
"discord": [
/* pieces */
],
"telegram": [
/* pieces */
],
},
},
}
```
Each surface is an ordered list of **pieces**; the engine renders each, drops
empties, and joins survivors with `sep`. A surface with no entry uses
`output.default`.
### Contract Paths
A piece reads values from the per-turn contract by dot-path. Absent values are
empty (so a `when` guard or a `|fallback` keeps the piece clean).
| Path | Meaning |
| ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `surface` | channel id (`discord`/`telegram`/etc.) |
| `agentId` / `chat_type` | owning agent id / chat surface kind |
| `model.id` / `model.display_name` / `model.provider` | model id / display name / provider id |
| `model.actual`, `model.resolved_ref` | provider/model ref actually used for the turn |
| `model.requested` | provider/model ref requested (before fallback) |
| `model.reasoning` | effort (`off` through `xhigh`) |
| `model.is_fallback` / `model.is_override` | bool: fallback used / model pinned |
| `model.override_source` / `model.auth_mode` | override source label / credential mode (`oauth`, `api-key`, `token`, `mixed`, `aws-sdk`, `unknown`) |
| `state.fast_mode` | bool: fast vs slow |
| `state.compactions` | compaction count for the session |
| `context.max_tokens` / `context.used_tokens` / `context.pct_used` | window budget / occupied tokens / 0-100 used |
| `usage.input_tokens` / `usage.output_tokens` / `usage.total_tokens` | turn aggregate |
| `usage.cache_read_tokens` / `usage.cache_write_tokens` | cache-read and cache-write tokens for the turn |
| `usage.has_tokens` / `usage.has_split_tokens` / `usage.has_total_only_tokens` | token display guards |
| `usage.cache_hit_pct` | cache-read share of total prompt tokens |
| `usage.last.input_tokens` / `usage.last.output_tokens` / `usage.last.cache_hit_pct` | final model call only (also has `cache_read_tokens`, `cache_write_tokens`, `total_tokens`) |
| `cost.turn_usd` / `cost.available` | estimated turn cost / whether a cost table resolved |
| `timing.duration_ms` | wall-clock turn duration |
| `identity.name` / `identity.emoji` / `identity.avatar` | agent identity name / emoji / avatar |
| `session.id` | session id |
(Provider rate-limit windows are **not** in this contract; there is no array-valued path today, so an `each` piece has nothing to iterate.)
### Verbs
Pipe a value through verbs left to right; a non-verb segment is the fallback.
| Verb | Effect | Example |
| --------------- | ------------------------------------- | --------------------------------- |
| `num` | compact count | `272000 -> 272k` |
| `fixed:N` | N decimals (default 2) | `0.0377` |
| `dur` | seconds to duration | `14820 -> 4h07m` |
| `pct` | append `%` | `96 -> 96%` |
| `inv` | `100 - x` | for used to remaining |
| `alias:TABLE` | lookup in `aliases`, echo if unlisted | `medium -> 🌗` |
| `meter:W:SCALE` | W-cell glyph bar over a 0-100 value | `[⣿⣿⠐⠐⠐]` (`meter:1` = one glyph) |
### Piece forms
- `{ "text": "📚 {context.max_tokens|num}" }`: literal + interpolation.
- `{ "when": "<path>", "text": "..." }`: render only if the path is truthy.
- `{ "map": "<path>", "cases": { "true": "⚡", "false": "🐌" } }`: value to glyph (a `_default` case covers unmatched values).
- `{ "each": "<array-path>", "item": "{label}" }`: iterate an array-valued path (no current contract path is an array).
### Example
```jsonc
{
"schema": "openclaw.usageBar.v1",
"scales": { "braille": "⠐⡀⡄⡆⡇⣇⣧⣷⣿" },
"aliases": { "reasoning": { "medium": "🌗", "high": "🌕" } },
"output": {
"surfaces": {
"discord": [
{ "text": "{model.display_name}" },
{ "when": "model.reasoning", "text": " {model.reasoning|alias:reasoning}" },
{ "map": "state.fast_mode", "cases": { "true": " ⚡", "false": " 🐌" } },
{
"when": "context.max_tokens",
"text": " | 📚 [{context.pct_used|meter:5:braille}]{context.max_tokens|num}",
},
],
},
},
}
```
renders e.g. `claude-sonnet-4-6 🌗 🐌 | 📚 [⣿⣿⣿⣿⣧]272k`.
## Providers + credentials
Usage is hidden when no usable provider usage auth can be resolved. Providers
supply their own usage-fetch logic; when that is unavailable OpenClaw falls back
to matching OAuth/API-key credentials from auth profiles, environment variables,
or config.
- **Anthropic (Claude)**: OAuth tokens in auth profiles. If the OAuth token lacks
`user:profile` scope, falls back to a `claude.ai` web session (`CLAUDE_AI_SESSION_KEY`,
`CLAUDE_WEB_SESSION_KEY`, or a `sessionKey=` cookie in `CLAUDE_WEB_COOKIE`) when set.
- **ClawRouter**: API key (`CLAWROUTER_API_KEY`). Shows a monthly budget window
when a budget is configured, otherwise a request/token/cost summary.
- **DeepSeek**: API key via env/config/auth store (`DEEPSEEK_API_KEY`).
Shows the provider-reported account balance as text instead of a percent-left
quota window.
- **GitHub Copilot**: OAuth tokens in auth profiles.
- **Gemini CLI**: OAuth tokens in auth profiles.
- **MiniMax**: API key or MiniMax OAuth auth profile. OpenClaw treats
`minimax`, `minimax-cn`, and `minimax-portal` as the same MiniMax quota
surface, prefers stored MiniMax OAuth when present, and otherwise falls back
to `MINIMAX_CODE_PLAN_KEY`, `MINIMAX_CODING_API_KEY`, or `MINIMAX_API_KEY`.
Usage polling derives the Coding Plan host from `models.providers.minimax-portal.baseUrl`
or `models.providers.minimax.baseUrl` when configured, and otherwise uses the
MiniMax CN host.
MiniMax's raw `usage_percent` / `usagePercent` fields mean **remaining**
quota, so OpenClaw inverts them before display; count-based fields win when
present.
- Window labels come from provider hours/minutes fields when present, then
fall back to the `start_time` / `end_time` span.
- If the coding-plan endpoint returns `model_remains`, OpenClaw prefers the
chat-model entry, derives the window label from timestamps when explicit
`window_hours` / `window_minutes` fields are absent, and includes the model
name in the plan label.
- **OpenAI (Codex/ChatGPT plan)**: OAuth tokens in auth profiles (`ChatGPT-Account-Id`
header sent when an account id is present). API-key-only OpenAI usage is not tracked.
- **Xiaomi MiMo**: two separate usage surfaces. Pay-as-you-go uses an API key
(`XIAOMI_API_KEY`); the Token Plan uses a separate key (`XIAOMI_TOKEN_PLAN_API_KEY`).
Neither currently reports quota windows.
- **z.ai**: API key via env/config/auth store (`ZAI_API_KEY` or `Z_AI_API_KEY`).
## Related
- [Token use and costs](/reference/token-use)
- [API usage and costs](/reference/api-usage-costs)
- [Prompt caching](/reference/prompt-caching)
- [Menu bar](/platforms/mac/menu-bar)