Summary
The current approval system supports three fixed modes (manual, smart, off). This feature request is for a fourth extensibility point: a custom approval hook — a user-defined script or command that Hermes calls to make the approve/deny decision, similar to how Claude Code's PreToolUse hooks work.
How Claude Code does it
In Claude Code, you can configure a shell command as a PreToolUse hook in settings.json. Before any tool runs, Claude Code calls that command with tool details on stdin and reads a JSON decision from stdout:
{ "decision": "deny", "reason": "Curl over plain HTTP to an internal host is not allowed in this context." }
The hook can be anything — a Python script, a local LLM call, a webhook to an external service. This makes the approval system fully extensible without forking the agent.
Requested behavior for Hermes
Add a custom approval mode (or a hook field alongside smart) that lets users define a command to call:
approvals:
mode: custom
hook: "/opt/data/scripts/approval_hook.py"
timeout: 30
on_deny_notify_gateway: true # post denial reason to the active platform channel
on_approve_notify_gateway: false # optionally post approval confirmation too (default: false)
When a dangerous command is detected, Hermes calls the hook with a JSON payload on stdin:
{
"tool": "terminal",
"command": "python3 -c \"import os; print(os.environ['HA_TOKEN'])\"",
"reason": "script execution via -c flag",
"session_id": "abc123",
"platform": "discord",
"user_id": "364815244633178123"
}
The hook returns a decision on stdout:
{ "decision": "approve" }
{ "decision": "approve", "reason": "Safe read-only env introspection, no exfiltration risk." }
{ "decision": "deny", "reason": "Printing the HA token to the terminal is not safe in this context." }
{ "decision": "escalate", "reason": "Uncertain — routing to human." }
- approve → execute; if
on_approve_notify_gateway: true and a reason is present, post it to the active platform channel (useful for audit trails)
- deny → block; if
on_deny_notify_gateway: true, post the reason to the active platform channel with optional human override prompt
- escalate → fall back to the existing manual gateway prompt
Why this matters / use case
Running a Home Assistant admin agent on Discord. The built-in smart mode works well for obvious cases, but:
- The smart LLM's judgement is a closed box — no visibility, no tuning
- Different agents have very different risk profiles. An HA agent legitimately runs
curl to local IPs and python -c env introspections constantly. A finance bot should never touch network calls. A fixed global policy can't capture this.
- With a pluggable hook, you can use a fast/cheap LLM with a custom system prompt tailored to the agent's context, make it context-aware, and have it post a clear approval or denial message to the Discord channel for full visibility into what the agent is doing and why
A simple hook script using any LLM API could be a 50-line Python file that gives far better context-aware decisions than a generic smart mode — and doubles as an audit log.
Prior art
- Claude Code
PreToolUse hooks: shell command called with tool details on stdin, returns JSON decision
- Standard Unix philosophy: the agent provides the interface, the user provides the logic
Summary
The current approval system supports three fixed modes (
manual,smart,off). This feature request is for a fourth extensibility point: a custom approval hook — a user-defined script or command that Hermes calls to make the approve/deny decision, similar to how Claude Code'sPreToolUsehooks work.How Claude Code does it
In Claude Code, you can configure a shell command as a
PreToolUsehook insettings.json. Before any tool runs, Claude Code calls that command with tool details on stdin and reads a JSON decision from stdout:{ "decision": "deny", "reason": "Curl over plain HTTP to an internal host is not allowed in this context." }The hook can be anything — a Python script, a local LLM call, a webhook to an external service. This makes the approval system fully extensible without forking the agent.
Requested behavior for Hermes
Add a
customapproval mode (or ahookfield alongsidesmart) that lets users define a command to call:When a dangerous command is detected, Hermes calls the hook with a JSON payload on stdin:
{ "tool": "terminal", "command": "python3 -c \"import os; print(os.environ['HA_TOKEN'])\"", "reason": "script execution via -c flag", "session_id": "abc123", "platform": "discord", "user_id": "364815244633178123" }The hook returns a decision on stdout:
{ "decision": "approve" } { "decision": "approve", "reason": "Safe read-only env introspection, no exfiltration risk." } { "decision": "deny", "reason": "Printing the HA token to the terminal is not safe in this context." } { "decision": "escalate", "reason": "Uncertain — routing to human." }on_approve_notify_gateway: trueand areasonis present, post it to the active platform channel (useful for audit trails)on_deny_notify_gateway: true, post the reason to the active platform channel with optional human override promptWhy this matters / use case
Running a Home Assistant admin agent on Discord. The built-in
smartmode works well for obvious cases, but:curlto local IPs andpython -cenv introspections constantly. A finance bot should never touch network calls. A fixed global policy can't capture this.A simple hook script using any LLM API could be a 50-line Python file that gives far better context-aware decisions than a generic
smartmode — and doubles as an audit log.Prior art
PreToolUsehooks: shell command called with tool details on stdin, returns JSON decision