Skip to main content

jev-curator

Communityv0.1.0

TypeSafe Jev semantic governance for Hermes' native Curator: classifies relationships between complete skill packages, produces content-hash-bound, fail-closed consolidation plans, and locally guards background destructive skill_manage calls while Hermes retains control of backups and mutations. A profile-scoped 60-second lifecycle debounce coalesces skill changes into dry refreshes.

Open in Hermes Desktop
hermes plugins install jev-curator

What it adds

Tools 1

jev_skill_relations

Hooks 2

on_skill_lifecyclepre_tool_call

README

From the reviewed commit 4e8626c; it updates when the author re-pins.

hermes-jev-curator

Typed, per-pair skill-relationship judgments for Hermes Agent's background skill curator, served by TypeSafe's Jev decision model.

Experimental release (0.1.0). The plugin loads and runs against stock Hermes, passes hermes plugins doctor / validate, and has been exercised against the live TypeSafe endpoint with whole, chunked, and locally unavailable pairs. The bundled synthetic corpus is still a contract self-check, not a model-quality benchmark. Default mode is read-only observe; run --apply still refuses unless mode: apply is set explicitly.

What it does

Hermes' background curator reviews agent-created skills on an idle timer: it marks stale skills, archives idle ones, and — with the LLM pass enabled — rewrites overlapping skills into umbrellas through the ledgered skill_manage tool. That pass decides in prose.

This plugin adds a typed layer in front of it:

  1. Deterministic candidates. A read-only inventory of curator-managed skills plus lexical top-k neighbor pairs. No model call; unchanged inputs produce identical pairs, ids, and order.
  2. Typed judgments. Each candidate pair uses one bounded request when both packages fit, or deterministic per-direction chunk requests otherwise. Every request answers a versioned question contract: an 8-way relation choice (duplicate, a_subset_of_b, b_subset_of_a, same_class, complementary, conflict, unrelated, insufficient_evidence) plus the applicable coverage, preservation, and conflict probability questions.
  3. Direct-edge graph. Judgments become typed edges between skill names, bound to both content digests. Merge plans are star-shaped — every absorbed member needs its own direct containment/duplicate judgment against the canonical; AB plus BC never merges C into A.
  4. Core keeps the pen. The plugin writes no skill text. Its apply path only archives sources that a validated plan says the canonical already preserves, through core's ledgered skill_manage, after a pre-apply snapshot. Umbrella prose stays core's job.

Hermes Curator integration

This plugin complements stock Hermes Curator; it does not replace core's idle scheduler or mutation machinery. Core still owns the managed-skill population, consolidation agent, snapshots, skill_manage, archive ledger, and rollback. The plugin adds an event-driven refresh: every successful create/install/edit/archive/restore/stale lifecycle mutation resets a profile-local 60-second debounce. After one quiet minute it runs run(apply=False) automatically when mode is not off and allow_content_egress: true. Repeated mutations collapse into one run; profiles remain isolated; a busy profile claim retries after another minute; plugin unload cancels pending timers. Mere skill loads do not schedule work.

Compatibility note: the one-minute debounce is a pragmatic workaround over Hermes' current per-event on_skill_lifecycle seam, not a replacement for core. The cleaner long-term API would be one host hook after Hermes has finished all lifecycle actions in a burst. We intend to propose that small upstream feature; until then, the debounce provides autonomous refresh without a core patch.

The timer preserves the originating profile's $HERMES_HOME and credential scope. Automatic runs never apply or archive skills, even in apply mode: observe refreshes evidence/reports, while guard and apply also replace guard_plans.json with fresh hash-bound plans.

Core's LLM consolidation pass is off by default even though Curator itself is enabled by default. This is the core curator.consolidate setting, separate from the plugin settings:

curator:
  enabled: true
  consolidate: true  # opt into automatic LLM umbrella passes; false by default

For a one-off pass, hermes curator run --consolidate enables consolidation for that invocation. When core starts that fork, it uses platform="curator" and the skills toolset. The enabled plugin then automatically:

  • adds the read-only jev_skill_relations tool to that toolset;
  • injects a Curator-only prompt requiring typed evidence before merges or absorptions;
  • records skill lifecycle events and schedules the debounced dry refresh; and
  • in guard/apply mode, checks background skill_manage mutations with the local pre_tool_call hook.

The plugin's commands are the immediate evidence/control path. After the quiet-minute refresh, core consolidation can use the fresh local plans directly; run hermes jev-curator run manually only when you do not want to wait for the debounce:

hermes jev-curator run
hermes curator run --dry-run --consolidate
hermes curator run --consolidate

The guard performs no network call. It permits only an exact authorized archive into an existing canonical; content patches, overwrites, removals, stale hashes, and unplanned archives are blocked. hermes jev-curator run --apply is different: it bypasses the LLM consolidation fork and directly executes the plugin's terminal-only, snapshot-first archive path for already-preserved sources.

Scope boundary: this integration governs the LLM consolidation fork's skill_manage calls. It does not change Hermes Curator's deterministic age-based stale/archive transitions.

Status (0.1.0)

component state
inventory, candidate generation, question contract, transport implemented, unit-tested
plugin manifest + register(ctx) (tool / prompt / lifecycle debounce + guard hooks / commands) implemented; doctor + validate pass; real-load verified
graph, plans, state (audit / cache / lock / reports), run --apply implemented and unit-tested; no production-library apply has been run
live Jev endpoint exercised end-to-end with whole and multi-request chunked evidence; no model-quality claim
frozen offline benchmark (benchmarks/) 21 synthetic relation/adversarial cases; self-check only, not a live-model quality claim

Stock-core integration

The plugin imports stock Hermes modules read-only. Core is not modified, patched, or monkeypatched; nothing in this tree requires a core change.

import used for
tools.skill_usage curated_report() / usage_report() rows, provenance(), is_curation_eligible()
agent.skill_utils iter_skill_index_files(), parse_frontmatter()
hermes_constants active-profile home plus context-local binding for delayed runs
agent.secret_scope preserve the originating profile's credential scope across the timer thread
cron.jobs referenced_skill_names() — cron-referenced skills are flagged protected
hermes_cli.runtime_provider credential resolution through Hermes' provider pool
hermes_cli.config get_env_value_prefer_dotenv() credential fallback
agent.redact mandatory redaction before egress; best-effort redaction in audit/report text
agent.curator_backup pre-apply skills snapshot (snapshot_skills)
tools.skill_provenance marks apply-mode writes as background-review origin
tools.write_approval refuses apply when staged replay would lose background-review provenance

Verified against Hermes Agent v0.21.3 using source checkout 522e121e.

Modes

Set under plugins.entries.jev-curator.settings.mode; read on every call, so an edit applies without a restart. Any value that is not one of the four below falls back to observe — never to a write mode.

mode behavior today
off no Jev requests and no automatic lifecycle run (inventory, candidates, and the deterministic baseline remain available manually)
observe (default) judgments + plans + reports; consented lifecycle mutations trigger the 60-second dry refresh; no skill mutation of any kind
guard the dry refresh installs current hash-bound relation plans and blocks background destructive skill_manage calls that are not an exact authorized absorption; foreground calls remain untouched
apply automatic refreshes remain dry; only explicit terminal hermes jev-curator run --apply archives sources of complete direct-edge, hash-bound plans via ledgered skill_manage, after a snapshot and digest re-check; requires core's _archived confirmation and verifies each source disappeared while the canonical stayed unchanged; refuses while skills.write_approval is enabled

Install

Install from GitHub:

hermes plugins install anpicasso/hermes-jev-curator/plugin
hermes plugins enable jev-curator
hermes gateway restart

For local development, copy plugin/ to $HERMES_HOME/plugins/jev-curator/ and add jev-curator to plugins.enabled. Plugins are profile-scoped: repeat the install for every $HERMES_HOME that should use it.

Configuration

Settings live in the host config, never in a file inside the plugin directory (that directory is a replaceable installed snapshot):

plugins:
  enabled: [jev-curator]
  entries:
    jev-curator:
      settings:
        mode: observe          # "off" | observe | guard | apply (quote off for YAML 1.1 parsers)
        provider: typesafe     # typesafe | openrouter | custom
        allow_content_egress: false  # set true only after accepting the disclosure below
        # base_url: https://jev.example/v1/systemone   # required for provider: custom
        # key_env: MY_JEV_KEY    # custom endpoints only
key default range meaning
mode observe off/observe/guard/apply see Modes
provider typesafe typesafe/openrouter/custom decision-endpoint preset
base_url https, port 443 complete Jev decision endpoint; custom only
jev_model provider default (jev-latest; OpenRouter: ~typesafe/jev-latest) backend model id; renamed because Hermes reserves the plugin setting root model
key_env environment-variable name custom endpoints only; anonymous when unset
allow_content_egress false boolean explicit consent required before skill text can be sent to any endpoint
timeout_seconds 25 1–120 one overall deadline per request, retries included
max_requests 50 1–500 per-operation request budget; chunk requests count individually
max_pairs 100 1–2000 per-scan candidate-pair budget
top_k 5 1–20 lexical neighbors kept per skill

Long pairs

Jev 1.13 allows 64,000 tokens per request, with a stricter 32,000-token limit for state plus the longest question. This plugin deliberately stays below both: pairs whose redacted package text fits the independent 160,000-byte serialized-state ceiling and 24,000-token estimate use one whole-pair request. The byte check counts JSON escaping and catches low-token whitespace; the token check catches dense Markdown/code. The 160,000 figure is bytes, not Jev tokens.

Larger pairs are planned per containment direction. The candidate source is split at package-file markers, then Markdown headings, then fixed-overlap hard boundaries, while the proposed container stays whole in every request. Each request records its side, chunk index/count, file scope, and containment question. A direction is unavailable when its container cannot fit whole.

Aggregation is fail-closed, not a vote: every planned chunk must answer; preservation and coverage use min, conflict uses max, and any conflict label wins. Certified directions determine duplicate/subset labels; disagreement becomes insufficient_evidence; an unmeasured direction is always 0.0. If neither direction is plannable, the pair becomes local insufficient_evidence without a network call. The planner first compares a conservative byte/token lower bound with the remaining per-operation request budget and refuses immediately when it cannot fit. Otherwise it materializes chunks and checks the exact count before sending; scans persist any refusal in scan["skipped"], while explicit pair review returns a bounded error. No partial calls are made.

Endpoint presets:

provider endpoint credential
typesafe (default) https://api.typesafe.ai/v1/systemone TYPESAFE_API_KEY in the active profile's .env / environment; a matching installed provider credential is also accepted
openrouter https://openrouter.ai/api/alpha/decisions hermes auth add openrouter, or OPENROUTER_API_KEY
custom your base_url only the configured key_env; anonymous when unset — it never inherits TypeSafe or OpenRouter credentials

Credentials resolve through Hermes' own chain (matching-host provider credential → active-profile .env → environment). A credential resolved for another host is never forwarded to the Jev endpoint. The optional jev-approvals companion registers the typesafe-jev Hermes credential provider; without it, TYPESAFE_API_KEY in the active profile's .env or environment is sufficient.

State and audit live under the active profile's $HERMES_HOME/jev-curator/ (bound the audit log with JEV_CURATOR_AUDIT_MAX_BYTES; 0 disables it). Files are mode 0600; audit rows and report text are redacted and bounded; the plugin refuses to write core-owned files (skills/.usage.json, skills/.curator_state).

Commands

hermes jev-curator status            # mode, inventory size, last run
hermes jev-curator scan              # inventory + candidate pairs (+ Jev judgments)
hermes jev-curator review NAME       # one skill's candidate pairs
hermes jev-curator graph             # edge summary with refusal counts
hermes jev-curator plan              # merge plans and blockers
hermes jev-curator run               # full pipeline, dry; writes a report
hermes jev-curator run --apply       # executes validated plans; needs mode=apply, terminal only
hermes jev-curator doctor            # route / inventory / mutation-default checks

Every subcommand takes --json for the bounded machine-readable payload. The /jev-curator slash command exposes the same subcommands but always runs dry: --apply is refused from chat.

Execution it rides (core's own):

hermes curator status                 # scheduler state, last run, skill stats
hermes curator run --consolidate      # the LLM umbrella pass this plugin informs
hermes curator ledger                 # per-mutation audit entries
hermes curator rollback [entry-id]    # restore one mutation (or the whole tree)

Verify the install:

hermes plugins doctor plugin --ci     # runtime discovery, manifest parse, import, register
hermes plugins validate plugin        # manifest-vs-registration diff, security scan
python3 -m pytest plugin/tests -q     # unit suite (determinism, graph gates, state, commands,
                                      # transport contract, synthetic merge corpus)

Network egress and data handling

The only network egress is the decision request itself. Manual and lifecycle-debounced requests are POSTed to the configured endpoint only when mode is not off and allow_content_egress: true. The default is no egress and no automatic run.

  • What leaves the machine after consent: one bounded state containing redacted skill names and either both complete packages or one complete package plus one labeled chunk, plus the question contract and configured model id. No package digest is sent; package text is never truncated.
  • Where it goes: the configured endpoint — https://api.typesafe.ai/v1/systemone by default, https://openrouter.ai/api/alpha/decisions for OpenRouter, or your custom URL. A third party therefore sees your skill content unless you point custom at your own host.
  • Redaction uses Hermes' own redactor and the request fails closed if that redactor is unavailable. Redaction is still a hygiene measure, not a confidentiality control.
  • Transport boundary: HTTPS on port 443 only; URL credentials, query strings, and fragments are rejected; cross-origin redirects are refused; requests and responses are capped at 2 MB; retries (429/529/5xx) are bounded and share one overall deadline.

What it never does

  • The tool never mutates. jev_skill_relations returns judgments only.
  • No direct skill writes. No code path writes skill files; mutations go through core's skill_manage so the ledger records them and hermes curator rollback can restore them.
  • No deletion. The apply path archives sources (delete with absorbed_into), which core implements as a recoverable move to skills/.archive/ (hermes curator restore <name>); a pre-apply snapshot is taken first, and a missing snapshot refuses the run. A partial failure returns the snapshot plus exact hermes curator restore <name> recovery commands.
  • No chat-driven mutation. /jev-curator run --apply is refused; apply needs a terminal, mode: apply, and validated plans whose digests still match.
  • No timer-driven mutation. Lifecycle debounce always calls run(apply=False); it may refresh evidence and local guard authorization, never archive or rewrite a skill.
  • No stale application. If any affected skill package changed after judgment, apply refuses and asks for a rescan. After every archive it re-reads only that source and canonical, requires the source to be gone, and requires the canonical digest to remain unchanged.

Restart

Python plugins load at discovery; there is no hot reload. After installing, enabling, or changing plugin code, restart the gateway (hermes gateway restart; named profiles: hermes --profile <name> gateway restart). Settings are re-read on every call, so config edits do not need a restart.

Uninstall

hermes plugins disable jev-curator    # stop loading it, keep the files
hermes plugins remove jev-curator     # remove the installed snapshot

Then delete $HERMES_HOME/jev-curator/ (audit, cache, state, reports) if you want it gone. Nothing else to undo: the plugin does not modify skills outside run --apply, config, or credentials, and no core file was changed.

Detailed documentation

Requirements

  • Hermes Agent v0.21.2+ (verified against v0.21.3 source checkout 522e121e)
  • Python 3.10+; standard library only — no third-party Python dependencies
  • A Jev credential only if you use TypeSafe or OpenRouter

Licence

MIT.

hermes-plugin-chrome-profiles Community 8

Switch Hermes browser tools to a named Chromium-family profile (Chrome, Edge, Brave), local or remote, over CDP.

Web & Browser
jev-approvals Community

TypeSafe's Jev decision model as Hermes' smart-approval reviewer only: it cannot generate text and refuses every other task. Supports TypeSafe direct, OpenRouter, and configurable Jev-compatible HTTPS endpoints with optional endpoint-specific authentication; validates typed answers and endpoint/redirect boundaries, redacts command credentials, and records bounded structured audit logs. Disclosure — each command routed to smart approval (redacted best-effort) and the operator's smart-policy text leave the machine for the configured third-party endpoint; provider or validation failures fail closed to ESCALATE.

Models
kiro-provider Community

Native Kiro model provider over HTTPS with AWS Builder ID / IAM Identity Center device login. Disclosure — dynamically registers an AWS OIDC public client named `hermes-kiro` during login and stores OAuth grants in Hermes' profile-scoped auth.json credential pool.

Models
oc-free-provider Community

Unofficial OpenCode Zen free-model transport for Hermes. Discovers live zero-cost tool-capable models by intersecting OpenCode Zen and models.dev over HTTPS, persists only the last successful intersection for restart fallback, and bidirectionally translates compatible OpenCode and Hermes tool schemas, calls, choices, and replayed history while Hermes executes tools locally. No OpenCode CLI, Node.js, npm, or npx dependency. Disclosure — prompts, tool schemas, tool results, and other conversation content leave the machine for OpenCode and the selected model provider; model availability, retention, data use, and permission are governed by their current terms.

Models

← Back to the catalog · catalog built Sep 21, 2026