feat(providers): add Hermes Agent provider#544
Merged
Conversation
Reads session-level usage from Hermes SQLite state databases (~/.hermes/state.db and per-profile state.dbs). Tracks input, output, cache read/write, reasoning tokens, stored costs, tools, shell commands, and inferred projects. - Provider reads both root and profile state databases - Cost fallback: actual_cost_usd > estimated_cost_usd > LiteLLM pricing - inferProject filters to user/system messages only - Discovery query capped at LIMIT 10000 - sanitizeProject handles repeated leading separators - All five review items from PR #386 addressed - Tested against real Hermes data (557MB state.db with 198 sessions) Closes #368. Supersedes #386.
…BUSY - Read the populated sessions.cwd column for project grouping; fall back to scraping the transcript, then the profile name. The current Hermes build no longer writes "Current working directory:" lines into messages. - Set costIsEstimated when the figure is the LiteLLM fallback (no recorded cost). - Re-throw SQLITE_BUSY from the discovery and read paths so a transient lock on the live state.db is retried instead of being cached as an empty result. - Tests: add the cwd column to fixtures; cover cwd grouping, the estimated flag, and tool-result tool_name extraction.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a Hermes Agent provider that reads session-level usage from Hermes' SQLite store (
~/.hermes/state.dband per-profile~/.hermes/profiles/<name>/state.db): tokens (input, output, cache read/write, reasoning), cost, models, tools, and shell commands.Rebased from #442 by @anthonyarmijo onto current main (the original was 87 commits behind and conflicting). Supersedes #442. Closes #368. PR #469 (a JSON-snapshot reader) was closed in favor of this SQLite approach.
Why SQLite, verified empirically
We installed Hermes and ran real sessions to confirm the storage model:
~/.hermes/state.dbis canonical. Per-sessionsession_*.jsonsnapshots are gated behindsessions.write_json_snapshots(false by default), so on a standard install that directory has no session files. Hermes' own docs say "state.db is canonical."sessionstable carriesinput_tokens,output_tokens,cache_read_tokens,cache_write_tokens,reasoning_tokens,estimated_cost_usd,actual_cost_usd,billing_provider,billing_mode,cost_status, andcwd, and they are populated on real runs.started_at/ended_atare Unix epoch-seconds floats.Polish applied on top of #442 (commit e80da31)
A five-agent adversarial validation pass (correctness, break-it, data-integrity, testing, consistency) ran against the real schema. It surfaced one ship-blocker, now fixed, plus the items below.
sessions.cwdcolumn, falling back to scraping a "Current working directory:" line from the transcript (older builds), then the profile name. The current Hermes build does not write that line into messages, so the previous text-only approach grouped everything under the profile name.costIsEstimatedis set when the cost is the LiteLLM fallback (Hermes recorded none).SQLITE_BUSYfrom the discovery and read paths is now re-thrown so a transient lock on the live, actively-writtenstate.dbis retried by the cache layer instead of being swallowed and cached as an empty result, which would silently drop a session's tokens and cost.Cost model
Priority is recorded
actual_cost_usd, then recordedestimated_cost_usd, then a LiteLLM estimate from the session token totals. Subscription-billed sessions (e.g. Codex via Hermes) record no cost, so they fall to the LiteLLM estimate, which is consistent with how CodeBurn prices Claude Code and Codex subscriptions; plan and proxy-path handle subscription coverage at a higher level. Note:costIsEstimatedis set but, like several existing providers (warp, kiro, codex, grok), has no downstream consumer yet, so it is not user-visible.Validation
tsc --noEmitclean.tool_nameextraction; fixtures updated to include the realcwdcolumn.Deferred decisions (validators upheld these)
parent_session_idis not special-cased. It is unproven whether Hermes rolls delegated child token usage into the parent row, and none of the verified sessions useddelegate_task. If a parent includes child tokens, counting both would double-count. Worth verifying with one delegate session before relying on it.Follow-ups (non-blocking)
api_call_count(today the dashboard "API calls" equals session count) or document it.input_tokensis cache-inclusive; if so, subtract cache-read before pricing the estimate.costIsEstimatedthrough to a consumer (cross-provider cleanup), or drop it.