Skip to main content

AgentGuard for Hermes Agent

Local spend caps, tool-call guarding, sub-agent limits and Ed25519-signed decisions for Hermes Agent. The PyPI package name remains agentguard-spend-hermes.

Install

Install into the same Python environment Hermes runs from, then explicitly enable the plugin in the same HERMES_HOME used by your CLI or gateway. For Hermes's default install location:

uv pip install --python ~/.hermes/hermes-agent/venv/bin/python agentguard-spend-hermes==0.3.0
hermes plugins enable agentguard-spend

The PyPI entry point and a directory copy under ~/.hermes/plugins/agentguard-spend share the plugin name. Hermes loads only one of them, and the PyPI entry point wins.

For a public repository assembled with the script below, replace owner/repo with its actual location:

uv pip install 'agentguard-spend>=0.15.4' 'pyyaml>=6.0'
hermes plugins install owner/repo
hermes plugins enable agentguard-spend

Install from PyPI. Hermes's install-time scan flags the rule data, because the rules quote the commands they block, so Hermes 0.21.0 refuses hermes plugins install owner/repo and --force does not override that verdict. Replacing an existing directory copy with hermes plugins install also needs --force.

Hermes displays declared Python dependencies but does not install them. If one is missing, or the policy or keys cannot be read, AgentGuard does not let Hermes run unguarded: it registers a lockdown before any setup step, and a failed setup leaves that lockdown refusing every model call and every tool call with this message, also written to the log:

AgentGuard setup failed: <reason>. Fix it, or set AGENTGUARD_DISABLE=1 to run without AgentGuard.

When setup succeeds the lockdown passes every call through. Enablement is required for both the PyPI entry point and directory installation. Restart the Hermes process you want to protect after installation or a policy change.

On first load AgentGuard creates agentguard/policy.yaml, a signing keypair and, on the first decision, agentguard/decisions.ndjson under HERMES_HOME (default ~/.hermes). The directory is owner-only, with a private key created using mode 0600.

The startup log identifies the active path:

AgentGuard 0.3.0: official middleware active (policy=...)

When the host has no register_middleware, the 0.2.x Anthropic/OpenAI client wrappers remain available and the log says legacy fallback active. That fallback provides legacy spend guarding; tool guards and delegation limits require the middleware API. AGENTGUARD_DISABLE=1 explicitly disables the plugin, including the lockdown. The legacy fallback still raises on a setup failure, and Hermes then runs without it.

What it blocks

The shared AgentGuard rule pack checks terminal commands, text typed into a background process with Hermes's process tool, Python code execution and file writes. It covers credential uploads (including GnuPG, SSH and cloud credentials), destructive deletion, shared Git history changes, unsafe infrastructure changes, sensitive file writes, secret-shaped arguments, security setting changes, remote package sources, disk formatting and database wipes.

For example, uploading ~/.gnupg with rsync is denied. Copying .env.example to .env within a project is allowed. Code execution checks literal calls to terminal, subprocess and file-writing functions as well as the shared native-code rules. Computed command or path arguments require approval.

Hermes's existing availability, approval and deny checks remain in its execution chain. AgentGuard can add a restriction; it never replaces a Hermes restriction with an allow. A gate error denies operations with effects and permits known read-only tools to continue through Hermes's checks.

How ASK works unattended

An ASK decision in an interactive CLI with a TTY uses Hermes's own approval prompt. Approval applies to one operation. A decline, timeout or prompt error denies it.

Cron, gateway, single-query and sessions without a TTY never wait for a prompt. They deny and record this exact reason:

AgentGuard: this needs a person's approval

Rules default to their shared settings. An operator can change a rule in policy.yaml, for example guard_rules: {GP009: warn} to ask before a sensitive write. stop denies, warn asks, and off disables that AgentGuard rule. Hermes's own checks still apply.

Spend and delegation policy

The default policy has a $5 daily soft cap, a $20 daily hard cap and a $2 per-minute hard cap. Amounts are integer USD cents. At the soft cap, request middleware swaps to the provider's mapped model only when that exact model id appears in the provider's catalog data Hermes keeps locally and costs less per input token and per output token than the current model. Otherwise it keeps the model, logs one line, and the hard cap stops spending. Execution middleware checks hard caps before calling the provider and records cost afterwards.

The default downgrade targets are Sonnet for Anthropic and OpenRouter, GPT-5 mini for OpenAI, and Gemini 3.1 Flash Lite for Gemini. A provider without a mapping never swaps. Prices come from the Spend price table, Hermes's own price table and Hermes's on-disk copy of OpenRouter's model list; AgentGuard never fetches them. Existing 0.2.x policies keep their caps and can add this mapping:

model_downgrades:
  openrouter: anthropic/claude-sonnet-4.6
  anthropic: claude-sonnet-4-6
  openai: gpt-5-mini
  gemini: gemini-3.1-flash-lite-preview

delegation:
  short_launches: 15
  short_minutes: 15
  long_launches: 40
  long_minutes: 120
  idle_seconds: 60

The 16th launch inside 15 active minutes, or the 41st inside 120 active minutes, requires approval. A batch counts each child. List, steer and stop operations do not count as launches. Accepted attempts count even if the downstream tool later fails, so failures cannot reset the limit.

An old policy's Anthropic downgrade target is translated to the default for direct OpenAI or Gemini calls and to OpenRouter's dotted id on OpenRouter, and then passes the same catalog and price checks. A configured requiredCapability stays closed because Hermes does not supply Spend capability claims.

The active clock advances during model/tool execution. Between calls it advances by at most idle_seconds; long idle gaps do not clear launch history. Counters are shared by child agents using the same runtime. Before each model call, AgentGuard reserves the call's estimated cost in every spend window under a short lock. The provider call runs without the lock, then the actual cost replaces the reservation; a failed, interrupted or cancelled call releases it. Reserved estimates count as spent, so calls running at the same time cannot spend the same remaining budget, and sessions, cron jobs and background sub-agents no longer wait for each other's model calls or for a pending approval prompt. Calls in flight together can pass a hard cap only by the amount their actual cost exceeds their estimates.

Signed decisions and privacy

Every guarded model call, tool decision and delegation is a signed Spend receipt in decisions.ndjson. The existing Spend schema holds the metadata under decision.governanceReceipt: kind (model_call, tool_call or delegate), rule_id, decision and a reason. Tool receipts also include tool_name, arguments_sha256 and the initial gate_decision, so an approved ASK remains visible.

Tool gates are recorded before execution. If Hermes itself then blocks a terminal, code, file-write or process call, or leaves it waiting for approval, a second receipt with stage: host_result records that restriction and the result is preserved. Results of other tools carry outside data and are not read for this.

AgentGuard never stores prompts, completions or raw tool arguments in this log. Argument hashes use SHA-256 over deterministic JSON. Rule scanning runs locally and never executes inspected commands. AgentGuard itself makes no network requests. The LLM call still goes to the provider configured in Hermes.

Model receipts identify the cost source:

Source Meaning
gateway_reported Provider response includes a cost in USD, including OpenRouter usage cost.
api_list_price_estimate Usage tokens are priced with Spend's local price table.
unpriced No provider cost and no usable local price or usage information.

Reported USD cost rounds up to integer cents. Estimates use Spend's rounding and include Anthropic cache tokens at base input price. These are local accounting records, not provider invoices.

Verify with the existing Spend command using your public key encoded as hex:

agentguard verify --trace ~/.hermes/agentguard/decisions.ndjson --public-key PUBLIC_KEY_HEX

The private key never belongs in that command. Existing 0.2.x receipts can remain at the beginning of the chain. New receipt appends use a local file lock so concurrent writers preserve sequence and signature linkage.

Limits

This is a lexical guard, not a sandbox. Encoded programs, files fetched and executed in separate calls, and arbitrary dynamic Python can exceed what local rule matching can infer. Python cannot determine every effect of an unknown function. Known limitations of the shared guard fixtures remain visible in parity tests.

Budget and launch counters live in the Hermes process and reset on restart. Separate processes do not share live caps, even when their signed decisions share a file. Use a separate HERMES_HOME for separate policies. Scope labels identify the budget; they do not split one policy into independent budgets.

Before a provider call, cost is a character/token estimate over the messages, system prompt, instructions and tool schemas, with max_tokens (or 4096) as the output. A provider's actual charge can exceed it. Unpriced calls cannot be charged accurately and are recorded as unpriced; previously reached hard caps still block them. A missing or malformed model payload is denied. A malformed usage response latches the model gate closed until restart. A signing failure is logged as an error and prevents subsequent model calls; tool gate failures follow the read-only rule above.

Protection applies at Hermes's registered middleware boundaries. The plugin must be enabled and importable; if Hermes cannot import it at all (for example its files are removed), Hermes runs without it. Calls outside those boundaries and the legacy fallback do not gain the new tool controls. Hermes's auxiliary model calls (for example context compression, session titles, vision, smart approvals, MCP sampling and mixture-of-agents) do not pass through llm_execution, so they are neither capped nor recorded. The codex_app_server runtime hands whole turns to Codex and bypasses both boundaries.

Hermes does not pass the working directory to middleware, so AgentGuard cannot tell which branch a git command runs on and treats every branch as shared: git reset --hard and git push --force always ask or deny, on any branch. They deny by default; set GP003 or GP004 to warn under guard_rules to be asked instead.

Assemble and test offline

From this monorepo:

node packages/agentguard-spend-hermes/scripts/export-guard-pack.cjs
uv run python packages/agentguard-spend-hermes/scripts/assemble-hermes-repo.py

This creates dist-hermes-repo/ containing the native plugin.yaml and root entry point plus the Python package. It does not publish anything and refuses to overwrite an existing folder. That folder is the root layout for an eventual owner/repo installation.

Run pytest and ruff in an environment containing the local Spend SDK, pytest, ruff and PyYAML. Set HERMES_TEST_PYTHON to the installed Hermes venv's Python and HERMES_TEST_ROOT to its source folder to include the real-loader smoke. The smoke uses temporary homes, fake provider calls and disabled network sockets. It writes no files to the Hermes installation.

The Node exporter copies rules and matcher constants from guard-pack.cjs and guard-dangerous.cjs. Tests require the checked-in JSON to match a fresh export and compare Python and Node scans and decisions on the same original fixtures. Rule changes belong in those shared sources first.

License

BUSL-1.1. Free for production deployments processing up to 10,000 enforcement calls per calendar month. Commercial licensing for higher volumes or service redistribution: invest@agentguard.run.

Certain AgentGuard technologies are patent pending in the United States. See https://agentguard.run/patents.

Metadata

Release files for agentguard-spend-hermes 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agentguard-spend-hermes 0.3.0
File Size Uploaded
agentguard_spend_hermes-0.3.0.tar.gz 111.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agentguard-spend-hermes 0.3.0
File Interpreter ABI Platform
agentguard_spend_hermes-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 168.7 kB

Release files / agentguard_spend_hermes-0.3.0.tar.gz

Download URL agentguard_spend_hermes-0.3.0.tar.gz
Size 111.4 kB
Tags Source
SHA-256 checksum
How to use checksums
94efcba45643bfb0fe2438adf150fc63a443ddeee9e022bcf8740e0c72e2227f
BLAKE2b-256 checksum
How to use checksums
373bf780aac1443e2f42ca271c685462bdfddc73bc7eee762022439f3689eda0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / agentguard_spend_hermes-0.3.0-py3-none-any.whl

Download URL agentguard_spend_hermes-0.3.0-py3-none-any.whl
Size 57.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c4a9ee8d6929f430c201080d4f18fd1b4cf179425c8da05e9510df65834d267f
BLAKE2b-256 checksum
How to use checksums
c49db43cddb0c9133fafdb612f6a52f809ed7dd50c3db2845987b0335b2222f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.4.1

2 release files

0.4.0

2 release files

This release

0.3.0 This release

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.4

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page