跳到主要内容

approval-ledger

❖ Communityv1.0.0★ 0

Local ledger of Hermes's approval prompts (dangerous commands, protected writes, MCP consent, smart-mode verdicts): /approval-log shows how often each rule asked, how many prompts were approved, denied or left unanswered, and how long a person took to answer, per rule or per surface, for the last 24 hours, any number of hours or days, the latest session or everything recorded; /approval-log recent lists the newest decisions.

Open in Hermes Desktop
hermes plugins install approval-ledger

What it adds

Hooks 2

pre_approval_requestpost_approval_response

README

From the reviewed commit d029b8e ↗; it updates when the author re-pins.

approval-ledger

A local ledger of the approval prompts Hermes raised, and an /approval-log command that shows which rules ask most, how the prompts ended and how long you took to answer.

Hermes asks before it runs a dangerous command (a recursive delete, sudo, a force push, ...), before a protected write, and for MCP consent. Each prompt is answered, denied, times out or is withdrawn, and then it is gone. approval-ledger subscribes to pre_approval_request and post_approval_response and keeps one row per decision, so you can see afterwards that one rule asked thirty times this week, that most prompts on your phone time out, or that smart mode denied something you then had to approve by hand.

Install

hermes plugins install approval-ledger --enable

Requires Hermes 0.21.5 or newer. No API key, no configuration, no dependencies.

Use

/approval-log             last 24 hours, by the rule that asked
/approval-log session     the most recent session that asked
/approval-log all         everything in the ledger
/approval-log 7d          the last 7 days (any number of hours or days: 6h, 30d)
/approval-log surfaces    add to any of the above to group by where it asked
/approval-log recent      add to any of the above to list the newest 10 decisions instead
/approval-log clear       delete the ledger

(/approvals is Hermes's own command for the approval mode; this plugin only reads.)

Example (/approval-log all, from the end-to-end test on Hermes 0.21.5: two prompts answered by hand, then smart mode denying one command, the person declining to override it, and smart mode approving another):

Approvals, all recorded:

pattern           asked  approved  denied  unanswered  median wait
recursive delete      5         2       3           0         0.6s

Smart-mode verdicts count as approved or denied; median wait is people's answers only.
  • asked counts decisions. A smart-mode deny that Hermes then offers you to override is two.
  • approved is once, session or always (or a smart-mode approve); denied is deny (or a smart-mode deny).
  • unanswered is a prompt nobody answered: it timed out, the turn ended or was interrupted before an answer, or the notification could not be delivered. The command did not run.
  • median wait is how long a person took to answer, from the prompt appearing to the answer. Smart-mode verdicts and unanswered prompts are left out of it.
  • pattern is the label of the rule that asked (recursive delete, sudo, ...), never the command. Grouped by surfaces, the first column is where it asked instead: cli, gateway (messaging platforms, the TUI and Desktop), smart, transport:<plugin>, or an MCP/vault consent surface.

/approval-log recent lists the newest decisions of the chosen window, newest first (same run):

Approvals, all recorded, newest 5 of 5:

when             pattern  surface         answer  wait
1s ago  recursive delete    smart  smart_approve  0.0s
1s ago  recursive delete      cli           deny  0.6s
2s ago  recursive delete    smart     smart_deny  1.0s
4s ago  recursive delete      cli           once  0.3s
4s ago  recursive delete      cli           deny  0.6s

A time window counts back from now (/approval-log 6h, /approval-log 7d surfaces, /approval-log 30d recent). The ledger keeps its newest 5,000 rows, so a long window on a busy install reaches back only as far as those rows do.

What it does not see

  • Commands Hermes never asks about: an approval you already gave for the session or permanently, approvals.mode: off, yolo mode, a sandboxed backend that skips the check, and one-shot (-q) or cron runs, where nobody could answer, so Hermes blocks or approves without a prompt.
  • A smart-mode verdict of "escalate" is not a decision: the prompt that follows it is.

What is recorded

One JSON line per decision in <HERMES_HOME>/plugin-data/approval-ledger/approvals.jsonl (per profile):

ts, session_id, surface, pattern, choice, outcome, decided_by, coalesced, wait_s.

Not recorded: the command, its description, the session key (it can hold a platform chat id), the deny reason. The hooks hand the plugin the command and description; the plugin drops both. The session key is held in memory only, for as long as the prompt is open, to pair the answer with its prompt.

The file is bounded: once it passes 2 MB it is rewritten to its newest 5,000 rows.

Security and footprint

  • register() only registers two hooks (pre_approval_request, post_approval_response) and one command (/approval-log).
  • Observer only: it cannot answer or change an approval. Each hook call notes a time or appends one line to the ledger, and nothing else.
  • No network access, no subprocesses, no downloads, no credentials read.
  • Writes only approvals.jsonl under its own plugin data directory. /approval-log clear deletes it.
  • The hooks never raise: if the ledger cannot be written, the failure is logged at debug level and the approval flow is not affected.
  • Standard library only.

Development

python -m pytest tests                                                # unit tests, no Hermes needed
PYTHONPATH=<hermes checkout> python tests/e2e/approval_ledger_e2e.py   # Hermes's own approval guard
hermes plugins validate approval-ledger

Changes

  • 1.0.0: first release.

MIT licensed.

aux-ledger❖ Community★ 0

Local ledger of Hermes's auxiliary LLM calls (titling, compression, vision, approval): /aux shows tokens, time and failures per task, model or provider for the last 24 hours, any number of hours or days, the latest session or everything recorded. Disclosure — writes auxiliary-call metadata (ids, models, timings, token counts, no prompt or response text) to plugin-data/aux-ledger/calls.jsonl, trimmed to 5,000 rows past 2 MB; no network

Tools
error-ledger❖ Community★ 0

Local ledger of failed provider calls (rate limits, overloads, timeouts, context overflows): /errors shows how many attempts failed per model, provider or cause, how many Hermes could retry, and the most common HTTP status, for the last 24 hours, any number of hours or days, the latest session or everything recorded; /errors recent lists the newest failures and how long ago each happened. Disclosure — writes failed-attempt metadata (ids, provider, model, HTTP status, Hermes's failure class, exception class name, timings; no error message, request or prompt text) to plugin-data/error-ledger/errors.jsonl, trimmed to 5,000 rows past 2 MB; no network

Tools
stream-speed❖ Community★ 0

How fast each model starts answering and how fast it writes: /speed shows the median and p90 time to first text and the median characters per second per model or provider, for the last 24 hours, any number of hours or days, the latest session or everything recorded, from the streaming hooks. Disclosure — writes per-response stream timings (ids, model, timings, character count, no text) to plugin-data/stream-speed/streams.jsonl, trimmed to 5,000 rows past 2 MB; atexit waits up to 1 s; no network

Tools

← Back to the catalog · catalog built Oct 8, 2026