khwarizmi-hermes-plugin

May 31, 2026 ยท View on GitHub

Hermes Agent adapter for the Mizan reliability scale. A thin layer that maps mizan onto Hermes's pre_llm_call, pre_tool_call, and on_session_end hooks.

๐Ÿ‡ธ๐Ÿ‡ฆ ู…ูุฎูŽุทูŽู‘ุท ุงู„ุทุจู‚ุฉ ุจุงู„ุนุฑุจูŠุฉ ู…ุน ู…ุซุงู„ ุญู‚ูŠู‚ูŠ ู…ูุชุฏุฑูู‘ุฌ ยท Arabic flow diagram + worked example: docs/diagram-ar.md

v0.2 โ€” all four operations wired: jabr (restore) + muqabalah (balance) run via mizan.preflight; qadiya (constraint-driven classification + dispatch) runs via mizan.ToolGate. Logic lives in the framework-agnostic mizan package; this repo is just the Hermes binding.


What it does today

HookAction
pre_llm_callRuns mizan.preflight (jabr.restore โ†’ muqabalah.balance) on the user message and injects the resolved/balanced trace as context. On contradiction, injects a hard "do not call any tool, ask one clarifying question" directive and stashes the conflict on the session. Carries the shared mizan receipt.
pre_tool_callGates in order: (1) contradiction caught this turn โ†’ block; (2) mizan.mcpscan tool-surface scan (multilingual/Unicode poisoning) โ€” audit/warn log only, block blocks on high severity (config mcpscan.mode, default off); (3) qadiya ToolGate โ†’ block if the tool call matches no allowed case; (4) KHWARIZMI_BLOCK_TOOLS env list โ†’ block. Otherwise audits.
on_session_endFlushes per-session state, writes a session_end audit record.

Every event is appended to ~/.hermes/plugins/khwarizmi/audit.jsonl (JSONL, append-only).

What it does NOT do (yet)

  • Tool gate is a tool-name allowlist today. mizan.ToolGate supports richer constraints (argument scope, target sensitivity) via a full qadiya CaseRegistry, but config.yaml currently only surfaces tool_policy.allow_tools. Multi-constraint policies are open work.
  • No LLM-call saving on contradictions. Hermes's existing pre_llm_call can only append context. It cannot replace the user message, and it cannot short-circuit the turn. So contradictions still cost one LLM call per turn โ€” the strong injected directive steers most current models toward clarification, but the model is still consulted. Closing this gap requires the upstream extension proposed in docs/upstream_discussion.md.
  • Default contradiction predicates are minimal. English-only, eight pairs. Fine for smoke tests, not for production traffic. Override via config.yaml.

Install

git clone https://github.com/Moshe-ship/khwarizmi-hermes-plugin
mkdir -p ~/.hermes/plugins
cp -r khwarizmi-hermes-plugin/khwarizmi ~/.hermes/plugins/

Hermes plugins are opt-in by default. Enable:

hermes plugins enable khwarizmi

That adds khwarizmi to plugins.enabled in ~/.hermes/config.yaml.

Install the packages the plugin imports (mizan plus the three primitives it wraps):

pip install git+https://github.com/Moshe-ship/jabr.git
pip install git+https://github.com/Moshe-ship/muqabalah.git
pip install git+https://github.com/Moshe-ship/qadiya.git
pip install git+https://github.com/Moshe-ship/mizan.git

Or, if you have the source checkouts at ~/Projects/{mizan,jabr,muqabalah,qadiya}/, the plugin auto-vendors them onto sys.path at import time โ€” no pip install needed for development.

Required core patch

Hermes commit fc8e4ebf calls get_pre_tool_call_block_message() at two sites in run_agent.py without forwarding session_id. Plugins that key state by session can't read it back. Apply patches/run_agent_session_id.patch:

cd ~/.hermes/hermes-agent
git apply /path/to/khwarizmi-hermes-plugin/patches/run_agent_session_id.patch

Or do the two-line edit by hand at lines ~7551 and ~7993:

# before
block_message = get_pre_tool_call_block_message(
    function_name, function_args, task_id=effective_task_id or "",
)
# after
block_message = get_pre_tool_call_block_message(
    function_name, function_args,
    task_id=effective_task_id or "",
    session_id=self.session_id or "",
)

Without that patch, the env blocklist still works, but the contradiction-triggered tool block silently no-ops because session_id arrives as "". The patch is also proposed upstream alongside the main pre_llm_call extension โ€” see docs/upstream_discussion.md.

Configure (optional)

~/.hermes/plugins/khwarizmi/config.yaml:

contradiction_predicates:
  - ["yes", "no"]
  - ["send", "cancel"]
  - ["send", "do not send"]
  - ["now", "later"]
  - ["public", "private"]
  - ["approve", "reject"]
  - ["delete", "keep"]

# jabr: pronoun โ†’ name resolution
referents:
  # her: Alice
  # him: Bob

# jabr: defaults for missing slots
defaults:
  # recipient: Alice
  # channel: "#general"

# qadiya tool gate (mizan.ToolGate): constraint-driven allowlist.
# When set, any tool NOT listed is escalated (blocked), never silently run.
# Omit this block to leave the gate inert.
tool_policy:
  allow_tools:
    - read_file
    - search
    - list_dir

audit_log: ~/.hermes/plugins/khwarizmi/audit.jsonl

Env knobs:

VariablePurpose
KHWARIZMI_BLOCK_TOOLSComma-separated tool names to hard-block via {"action": "block"}.
KHWARIZMI_AUDIT_OFFSet to 1 to disable the audit log.

Verify

tail -f ~/.hermes/plugins/khwarizmi/audit.jsonl

In a fresh Hermes session, expect:

  • one preflight record per turn (carrying the full mizan receipt: restore + balance stages)
  • one tool_call record per tool the agent dispatches
  • a contradiction_caught record when the user input contains a configured contradiction predicate
  • a tool_blocked_contradiction record if the model still attempts a tool after a contradiction was caught
  • a tool_blocked_gate record when tool_policy.allow_tools is set and the agent attempts a tool outside the allowlist
  • a session_end record on session shutdown

Upstream proposal

The full proposal โ€” structured returns for pre_llm_call (allow / rewrite / early-exit) โ€” is in docs/upstream_discussion.md. This plugin is the reference implementation that motivates that discussion: every part of the pattern works on the existing hooks except the LLM-skip path on deterministically caught conditions.

Honest framing

The benchmark behind this work is intentionally a should-clarify set, not a should-call-a-tool set. Across 12 current OpenRouter models on 91 ambiguous cases, the four-step pattern (in its unrestricted reference form, not this hook-limited subset) raised mean safe-outcome accuracy from 95.1% to 98.4% and resolved 16.5% of cases before any LLM call. Local plugin behaviour will differ on different traffic distributions. Full reports: Moshe-ship/case-eval.

License

MIT. See LICENSE.