The cache-hit manual
A clearer view of your agent’s cache.
Install the plugin, make the sidebar your own, and understand what each indicator means. Supports Codex CLI, Claude Code, OpenCode, and Antigravity CLI on macOS, Linux, and Windows through Git Bash.
Quick start
You need Herdr, Bash, and jq. Native macOS Bash 3.2 works. On Windows, install Git for Windows and make its sh.exe available on the Windows PATH; jq must also be on the PATH seen by Git Bash. WSL2 remains an option. Python 3.6+ is recommended for exact Codex transcript selection and needed for OpenCode, sorting, or Codex/Claude session recovery without a native session ID.
Install the plugin
Install directly with Herdr:
herdr plugin install e-kotov/herdr-cache-hitHerdr will automatically clone the repository and run the build step to download and checksum-verify the precompiled helper binary for your platform.
Manual / Development Install
git clone https://github.com/e-kotov/herdr-cache-hit.git
cd herdr-cache-hit
# Download precompiled helper binary (or compile locally with ./scripts/build-agy-usage.sh)
./scripts/download-helpers.sh
# Link to Herdr
herdr plugin unlink cache-hit 2>/dev/null || true
herdr plugin link .
herdr plugin listAdd a $cache row to your existing [ui.sidebar.agents] configuration in ~/.config/herdr/config.toml. A full example appears in Sidebar styling. Reload your changes:
herdr server reload-configHow updates work
cache-hit is an event-driven metadata scanner for Herdr. - No Background Daemon: Herdr events start watch.sh; no persistent scanner process is required. - Single Refresh Timer: While a cache is active, an enabled Codex pane is present, or an enabled agent pane awaits its first usage record, one lightweight sleep process wakes at the earlier of 15 seconds or the next expiration display transition. Session recovery and late usage can appear without a focus event; expired caches alone do not keep the timer running. - Privacy: Content is processed locally only as necessary to extract usage metadata and is not intentionally extracted, retained, logged, or transmitted. Only token usage counters, session identifiers, model names, and provider identifiers are tracked.
Configuration Reference
Configuration is stored in config.json inside the plugin’s configuration directory:
herdr plugin config-dir cache-hitIf config.json does not exist, safe built-in defaults are used. Changes to config.json take effect on the very next Herdr scan without needing a server restart.
Global Settings
| Setting | Type | Default | Description |
|---|---|---|---|
hot_symbol |
string | "" |
Symbol or emoji prepended to the countdown clock when the cache is active and healthy (> expiring_threshold_seconds). E.g. "♨️" or "". |
expiring_symbol |
string | "⏰" |
Symbol or emoji prepended when remaining cache lifetime is less than or equal to expiring_threshold_seconds. E.g. "⏰" or "⚠️". |
cold_symbol |
string | "❄" |
Symbol shown immediately before retained token counts when the cache has expired. Set "" to hide it. |
cache_warmer_symbol |
string | "↻" |
Symbol placed immediately before the countdown for an armed Codex, AGY, or Claude session. Set "" to hide the indicator. |
cache_warmer_allow_focused_pane |
boolean | false |
Allow warming the currently focused pane. This only relaxes the focus guard; the session must still be opted in and confirmed idle. Per-agent values can override the root setting. |
cache_warmer_allow_nonempty_prompt |
boolean | false |
Allow warming when text is already present in the agent’s prompt editor. This only relaxes the composer guard. Herdr submits the warm prompt through terminal input, so the existing draft may be submitted together with it. Per-agent values can override the root setting. |
expiring_threshold_seconds |
integer | 300 |
Warning threshold in seconds (default 5 minutes). At or below this, the warning symbol appears. |
bold_time |
boolean | true |
When true, converts countdown clock digits into Unicode mathematical sans-serif bold characters (𝟬-𝟵) for visual punch. |
bold_threshold_seconds |
integer | 300 |
Countdown threshold in seconds under which clock digits turn bold. Keeps healthy caches sleek and non-bold, turning bold only when expiring. |
timezone |
string | "" |
IANA timezone name (e.g. "Europe/Berlin", "America/New_York"). When empty, respects HERDR_PLUGIN_TIMEZONE or the local system timezone. |
display_agent |
string | "auto" |
Controls injection of cache stats into Herdr’s --display-agent metadata ("auto", "always", or "never"). In "auto" mode, stats are injected only when terminal width \(\le\) mobile_width_threshold or under Termux, keeping desktop multi-row sidebars clean and free of duplicate badges. |
mobile_width_threshold |
integer | 64 |
Terminal column width at or below which Herdr collapses into single-line mobile layout. |
Per-Agent Settings
Supported agent keys: agy (Antigravity CLI), claude (Claude Code), codex (Codex CLI), opencode (OpenCode).
{
"codex": {
"enabled": true,
"cache_warmer_sessions": [],
"cache_warmer_margin_seconds": 300,
"cache_warmer_max_per_session": 0,
"cache_warmer_duration_hours": 0,
"show_deadline": true,
"show_read_tokens": true,
"show_write_tokens": false,
"show_percentage": true,
"show_model": false,
"ttl_ceiling": 3600
},
"agy": {
"enabled": true,
"cache_warmer_sessions": [],
"cache_warmer_margin_seconds": 60,
"cache_warmer_max_per_session": 0,
"cache_warmer_duration_hours": 0,
"show_deadline": true,
"show_read_tokens": true,
"show_write_tokens": false,
"show_percentage": false,
"show_model": false,
"ttl_ceiling": 3600
},
"claude": {
"enabled": true,
"cache_warmer_sessions": [],
"cache_warmer_margin_seconds": 60,
"cache_warmer_max_per_session": 0,
"cache_warmer_duration_hours": 0,
"show_deadline": true,
"show_read_tokens": true,
"show_write_tokens": false,
"show_percentage": false,
"show_model": false,
"ttl_ceiling": 3600
}
}| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Enable cache tracking for this agent. Set false to clear this agent’s cache token. |
cache_warmer_sessions |
array of session IDs | [] |
Codex, AGY, and Claude. Sessions explicitly armed with prefix+u; each eligible session may receive a short normal turn near the current countdown deadline. The experimental turn is appended to the conversation. |
cache_warmer_global_enabled |
boolean | false |
When true, all Codex, AGY, and Claude sessions are armed, including sessions opened later. The command palette item “Switch between global and per-agent warming” toggles this mode; turning it off returns to saved per-session settings and does not turn those settings off. No global shortcut is bound by default. |
cache_warmer_global_excluded_sessions |
object of agent to session ID arrays | {} |
Per-session exclusions while global mode is on. prefix+u toggles the focused session in or out of the global set. |
cache_warmer_margin_seconds |
integer | Codex 300, AGY and Claude 60 |
Attempt a warm turn when the displayed cache countdown reaches this many seconds; accepted range is 30–600 seconds. Claude warming requires cache lifetime evidence from a 5-minute or 1-hour cache write. |
cache_warmer_max_per_session |
integer | 0 |
Codex, AGY, and Claude. 0 allows indefinite warming while the session is opted in; set 1–3 to cap warm-turn attempts per session. |
cache_warmer_duration_hours |
integer | 0 |
Codex, AGY, and Claude. 0 allows indefinite warming; set 1–8760 to stop warming that session this many hours after its first warmer attempt. Applies at the root or per-agent level. |
show_deadline |
boolean | true |
Include the estimated expiration countdown time (~15:44). |
show_read_tokens |
boolean | true |
Include read/cached tokens counter (e.g. ⇣95.4k). |
show_write_tokens |
boolean | false |
Include cache-creation tokens counter (e.g. ⇡12.3k). |
show_percentage |
boolean | Agent-dependent | Show cache hit percentage (99%). Defaults true for Codex/OpenCode, false for AGY/Claude. |
show_model |
boolean | false |
Include model name in the token string. |
ttl_ceiling |
integer | 3600 |
Hard upper bound in seconds on learned prompt-cache survival time. |
For Codex, the countdown starts from a 30-minute baseline, matching OpenAI’s baseline used by this plugin. The plugin shortens that estimate only after at least three recorded survival observations below 30 minutes for the same provider and model. Longer observations do not extend the Codex countdown. The countdown is still an estimate: cache misses can also result from a changed prompt prefix or routing, so it is not a guarantee of cache eviction time.
Experimental cache warming
The experimental warmer is off by default. When enabled, it sends a normal user turn through Herdr; this adds a short prompt and response to the agent conversation and incurs that agent’s normal usage. Before submission it rechecks the session and requires an idle or done status plus an idle harness activity signal. By default the pane must be unfocused and the prompt editor empty (Ask Codex to do anything for Codex; a standalone > prompt for AGY; an empty ❯ prompt for Claude). The two cache_warmer_allow_* settings can independently relax those last two guards; unknown activity state still skips. For Claude, a session whose latest reply is an API rejection (for example a usage limit) also skips until a later reply succeeds; transient server errors do not block. Claude cache warming follows the lifetime indicated by recorded 5-minute or 1-hour cache writes and uses a 60-second default margin. Working parents, including parents waiting on subagents, are skipped. Warmed intervals are excluded from survival observations. Status and prompt checks cannot make PTY submission atomic with concurrent user input. Enabling nonempty-prompt warming may submit the existing draft together with the warm prompt, so enable it only when that behavior is acceptable.
With the configured prefix (Ctrl+B), toggle warming for the focused Codex, AGY, or Claude session with u. Toggle global warming for all supported warmer sessions, including sessions opened later, from the command palette action Switch between global and per-agent warming. No global-warming shortcut is bound by default. While global mode is on, u excludes or restores only the focused session. Turning global mode off restores the saved per-session settings. The active cache display gains the configured cache_warmer_symbol (default ↻) immediately before the countdown when that session is armed. A warm turn is sent only if the pane is idle and the estimated deadline nears; focus and prompt-editor requirements follow the two independent cache_warmer_allow_* settings. Each toggle also shows a quiet Herdr notification describing the resulting setting. By default, warming continues indefinitely. A finite cache_warmer_duration_hours starts at the first warmer attempt for each session; after that duration, the session is no longer warmed and its armed marker disappears.
Token Reference
The plugin emits the following pane tokens to Herdr:
| Token | Description | Example Output |
|---|---|---|
$cache |
Unified string combining status, countdown, percentage, and token counts without middle-dot separators. Cold output omits the last-request percentage. | Hot: ~15:44 99% ⇣95.4kExpiring: ⏰~𝟭𝟱:𝟰𝟰 99% ⇣95.4kCold: ❄ ⇣95.4k |
$cache_status |
Status symbol and countdown clock. | ~15:44, ⏰~𝟭𝟱:𝟰𝟰, or ❄ when cold |
$cache_pct |
Formatted cache hit percentage; cleared when cold. | 99% |
$cache_pct_num |
Raw integer hit percentage for numeric comparisons; cleared when cold. | 99 |
$cache_remaining_secs |
Seconds remaining until expiration; cleared when cold. | 240 |
$cache_tokens |
Just the read/write token counters. | ⇣95.4k |
$cache_state |
Lifecycle state identifier. | hot, expiring, or cold |
cache_deadline |
Unix epoch integer timestamp of expiration. | 1788877732 (cleared when cold) |
Agent Sorting & Keybindings
The plugin includes bin/herdr-cache-view, a declarative view manager that interfaces with Herdr’s JSON-RPC socket API (agent.view.set and agent.view.clear).
View Modes
expiry: Sorts active sessions with live prompt caches by earliest expiration deadline first (cache_deadline asc), keeping expiring sessions at the top. Cold sessions fall back cleanly to Herdr priority mode (attention desc,state_change_seq desc).native: Herdr’s default interactive sort mode.
CLI Usage
# Toggle between expiry sorting and native sorting
herdr-cache-view toggle
# Explicitly set view
herdr-cache-view set expiry
herdr-cache-view set native
# Check current active mode
herdr-cache-view get
# Restore saved mode (run on startup)
herdr-cache-view restoreHerdr Keybinding Integration
Add a keybinding in ~/.config/herdr/config.toml to toggle cache sorting with prefix+s:
[[keys.command]]
key = "s"
command = ["bash", "-c", "herdr-cache-view toggle"]
# Remap Herdr settings to prefix+S (shift+s)
[keys]
settings = "S"The active mode is saved in sort_mode.json and automatically restored whenever Herdr boots via the plugin’s [[startup]] hook.
Agent Adapters & Data Sources
| Agent | Extraction Method | Required Helper / Dependencies |
|---|---|---|
| Codex CLI | Current transcript pointer from Codex’s SQLite database, with rollout fallback | bash + jq; python3 3.6+ recommended for exact branch selection and required for missing-session recovery |
| Claude Code | Project transcript analysis (~/.claude/projects/) |
bash + jq; python3 3.6+ for missing-session recovery |
| OpenCode | SQLite database (~/.local/share/opencode/opencode.db) |
python3 |
| AGY / Antigravity CLI | Multi-tier fallback (Statusline → Go helper → Transcript) | Optional Go helper (agy-usage-*) or statusline hook |
Codex Session Recovery
Since v0.1.25, the adapter follows Codex’s current transcript pointer in its local database. Prompt rewind/edit can create multiple transcripts with the same thread ID; reading the pointer prevents a discarded branch from supplying stale cache clocks. Python 3.6+ enables this read-only lookup. Without Python or an available database, Bash and jq select the matching transcript with the latest valid usage timestamp; that fallback cannot distinguish a discarded branch with newer usage.
The adapter supports both token_usage_record.payload.usage and event_msg.payload.info.last_token_usage (token_count). It uses per-request cache counts, ignores quota-only events, and reads model/provider metadata from the preceding turn context and session metadata. CODEX_SESSIONS_DIR takes precedence over HODEX_SESSIONS_DIR, then CODEX_HOME.
When Herdr has no native Codex session ID, the optional Python helper inspects pane process-info. An explicit codex resume <id> selects that session. Otherwise, a unique root rollout matching the process launch time and exact directory is selected. When several roots are active, cumulative input/output counters in the pane’s terminal title can identify a unique matching rollout. These totals are used only for session identification, never for the cache-hit percentage. Interactive resume can fall back to a unique root with usage since process launch in that directory only when no other Codex pane shares it; an empty bootstrap thread is excluded when a resumed thread has real usage. Subagents and sessions already assigned to other panes are excluded. Ambiguous matches leave the cache blank rather than display another session’s counters. This publishes only cache metadata; it does not change Herdr’s native session identity.
The fallback was informed by herdr-agent-usage’s Codex adapter, with additional guards for same-directory panes and subagents.
AGY / Antigravity CLI Architecture
Because AGY CLI transcripts do not serialize token metrics to disk, the plugin uses a 3-tier fallback strategy: 1. Tier 1 — Live Statusline Sidecar (Fastest): The supplied scripts/agy-statusline-capture.sh wrapper writes real-time prompt cache stats to ~/.cache/herdr-cache-plugin/agy-statusline/<sid>.json without SQLite parsing. 2. Tier 2 — Go SQLite Helper (agy-usage-*): If the statusline sidecar is absent, the plugin invokes the compiled helper to extract token metrics directly from AGY’s internal SQLite database (~/.gemini/antigravity-cli/conversations/<sid>.db). Precompiled binaries are provided for macOS, Linux, and Windows. 3. Tier 3 — Transcript Fallback: If neither is available, it attempts to read transcript.jsonl (used by AGY Web IDE).
[!NOTE] If you don’t use AGY CLI: You do not need the Go helper or any statusline scripts. Codex, Claude Code, and OpenCode work completely out of the box with standard system tools (
bash,jq, and optionallypython3).
For installation instructions for the tested wrapper, see the Antigravity CLI (AGY) Integration Guide.
Troubleshooting & Diagnostics
Inspect Live Tokens
Run herdr pane list to view the tokens currently assigned to each pane:
herdr pane list | jq '.result.panes[] | {agent, tokens}'Inspect Observations
Survival observations are stored in:
cat "${XDG_STATE_HOME:-$HOME/.local/state}/herdr/plugins/cache-hit/observations.json"Verify Scripts & Tests
bash -n watch.sh
shellcheck watch.sh lib/*.sh bin/herdr-cache-view scripts/*.sh
bash tests/test_watch.shResources
Example configuration · Changelog · Release notes · Report an issue · Source user guide