8000 GitHub - magnus919/hermes-cashew: Memory provider for Hermes Agent to work with rajkripal/cashew · GitHub
[go: up one dir, main page]

Skip to content

About

Memory provider for Hermes Agent to work with rajkripal/cashew

Resources

Contributing

Security policy

Stars

17 stars

Watchers

1 watching

Forks

Repository files navigation

hermes-cashew

A Hermes Agent memory provider plugin that stores conversation context in a local Cashew thought graph with semantic search and automatic context recall. Get from zero to a working install in under five minutes.

The plugin auto-generates cashew.json with defaults on first load, can use an explicit Hermes auxiliary.memory mapping for LLM-powered extraction, adds forest-level insight extraction via on_pre_compress, and runs graph consolidation on a persistent Hermes cron schedule.

Integrity audit

Existing profiles can be inspected without opening a writable database:

python -m plugins.memory.cashew.integrity /path/to/brain.db

The audit uses a canonical shared maintenance lease and SQLite URI read-only mode. It reports schema and privacy-safe provider fingerprints, ordinary embedding validity, vector-index parity when the installed extension can be verified, orphan rows, referential graph defects, and permanence contradictions. Large scans use fixed row, byte, and deadline budgets; a cutoff is reported as audit_incomplete rather than presented as a complete audit. It never runs schema migrations, decay, consolidation, embedding services, or repair as a side effect. Reports include only bounded counts and reason codes; historical merge intent remains an explicit manual-review item because it cannot be reconstructed from the stored graph safely.

The explicit operator apply surface is opt-in and backup-backed:

python -m plugins.memory.cashew.integrity --apply --confirm /path/to/brain.db

The command takes the canonical exclusive maintenance lease, reads the stored model identity, writes and verifies a SQLite backup under the profile's backups/ directory, starts a write-excluding transaction, delegates bounded repairs to Cashew, verifies the result before and after commit, and reports the backup path. A failure before commit rolls back the repair transaction while retaining the backup. Missing profiles, missing model identity, incompatible requested identity, unavailable upstream APIs, and lock/backup failures all fail closed. The workflow does not touch HOME or change journal mode. It preserves the maintenance epoch and does not lock or invalidate the separate embedding cache: targeted repairs preserve the stored model identity, and that cache maps content to same-model vectors rather than graph rows. A future repair that changes model identity must use the existing graph-to-cache migration boundary instead of this workflow.

By default Cashew attempts its information-preserving repairs. Operators can restrict the work by repeating --action; use --max-items and --batch-size to bound a run. Contradictory permanence state remains report-only unless --permanence-policy preserve_permanent or preserve_decay is chosen explicitly. Missing or invalid embeddings require a compatible embedding callable through the Python API; the CLI does not construct one or perform blanket re-embedding.

The selected immutable Cashew composite also provides the connection-owned API for applications that already own the full maintenance boundary. Such callers may use inspect_integrity(conn, ...) and, only after confirm=True, apply_integrity_repairs(conn=conn, ...); they retain ownership of backup, locking, transaction, commit, rollback, verification, and close. Older Cashew installations without the API remain fail-closed. Neither mode reconstructs merged facts or restores decayed nodes automatically; retain the original database and generated backups for manual review.

Prerequisites

  • Hermes Agent installed
  • Cashew engine included with the plugin; no separate cashew-brain install
  • SQLite 3.35 or newer, required for Cashew's legacy v1 schema migration
  • sqlite-vec — enables vector similarity search; installed as a runtime dependency

Install

hermes plugins install magnus919/hermes-cashew

This installs into the active Hermes home under plugins/cashew/ (default: ~/.hermes/plugins/cashew/). Hermes installs ordinary index dependencies; the reviewed Cashew engine ships inside the plugin. No manual direct-URL install is needed after installation or a Hermes environment rebuild.

The bundled engine is the selected composite commit fcb4919ac37144bfbeb822eaafc668a4bdceb791. Its schema, retrieval, session, sleep, and integrity algorithms remain upstream-owned. A private namespace prevents an older separately installed cashew-brain from replacing it. The bundle includes the upstream MIT license, a pristine source snapshot, and a file-by-file provenance inventory. Absolute intra-engine imports are mechanically relocated, and the standalone migration script's sys.path bootstrap is removed. See upstream compatibility for the source and replacement policy.

SQLite 3.35 or newer is required for legacy v1 migrations. To audit the installed bundle and linked SQLite runtime, run scripts/verify-cashew-baseline.py with the Python interpreter selected by Hermes for the plugin. Version metadata alone does not identify the selected engine.

Optional diagnostics

Diagnostics remain disabled unless explicitly enabled. The normal plugin install uses runtime dependencies only. To enable either optional Sentry or OpenTelemetry diagnostics, install the tracing extra into Hermes's environment first:

uv pip install --python ~/.hermes/hermes-agent/venv/bin/python3 \
  'hermes-cashew[tracing]'

Then set HERMES_CASHEW_SENTRY_DSN for Cashew's isolated diagnostics worker, or set HERMES_CASHEW_OTEL_ENABLED=1 for Cashew spans. Generic SENTRY_* and OTEL_* host settings do not enable Cashew diagnostics. Events and spans contain only fixed operation codes and allowlisted bounded metadata.

After install, run setup and restart the gateway:

hermes memory setup
hermes gateway restart

Register with Hermes

After installing, set cashew as the active memory provider:

hermes config set memory.provider cashew
hermes gateway restart

Or use the interactive setup (v0.2.0 now includes cashew in the provider picker):

hermes memory setup

Update and rollback

Update through Hermes:

hermes plugins update cashew
hermes gateway restart
hermes memory status

hermes update and environment rebuilds retain the engine as part of the plugin; there is no separately installed source dependency to restore. A catalog pin may need to advance before a catalog installation selects a newly merged fix. Updating the separately installed PyPI cashew-brain does not update the bundled engine.

To roll back, select a previously tested plugin revision through Hermes. Keep profile configuration and graph backups; uninstalling the plugin is unnecessary for routine updates. Older plugin revisions may still require their documented manual dependency setup and should not be assumed compatible with current data.

Zero-Config Startup

hermes-cashew works out of the box — all 17 persisted configuration fields have sane defaults and are backed by current runtime behavior. On first agent startup, the plugin creates cashew.json in the active Hermes home with the full default configuration. It never edits Hermes config.yaml; until an auxiliary role is explicitly configured there, extraction remains heuristic-only.

Edit cashew.json under the active Hermes home only if you want to override specific defaults. Automatic startup does not overwrite an existing file; an explicit hermes memory setup save can update and normalize it:

# Optional: override individual defaults
cat > "${HERMES_HOME:-$HOME/.hermes}/cashew.json" << 'EOF'
{
  "recall_k": 10,
  "think_interval": 15,
  "user_domain": "user"
}
EOF

Full Config Reference

Core

Key Default Description
cashew_db_path cashew/brain.db Path to SQLite DB, relative to hermes_home
embedding_model thenlper/gte-large Sentence-transformers model for embeddings (1024-dim)
embedding_device cpu Sentence-transformers device; cpu avoids unstable native accelerators
llm_aux_role memory Hermes auxiliary role for LLM-powered extraction; requires auxiliary.memory in config.yaml
auto_extraction true Auto-extract knowledge from conversation turns
sync_queue_timeout 30.0 Seconds to wait for sync worker drain on shutdown

Retrieval

Key Default Description
recall_k 5 Context fragments returned per query
prefetch_k 3 Nodes to pre-warm into context on each turn
prefetch_cues 3 Cue phrases to send to LLM for prefetch generation

Domains & Classification

Key Default Description
user_domain user Domain label for user messages
ai_domain ai Domain label for AI messages

Sleep Cycle

Key Default Description
sleep_cycles true Enable the upstream Cashew sleep cycle through the Hermes adapter (cross-linking, dedup, GC, dreams)
sleep_schedule "every 12h" Cron schedule for sleep cycle
sleep_max_nodes 2000 Max nodes per sleep cycle tick
think_cycles true Enable periodic insight generation (think cycle)
think_interval 10 Turns between think cycle runs (0 = disable)

Cashew validates the effective JSON and CASHEW_* configuration before use. recall_k and prefetch_k allow 1–20; prefetch_cues allows 0–20; think_interval allows 0–10,000; sleep_max_nodes allows 1–2,000; and sync_queue_timeout must be finite and between 0 and 300 seconds. Invalid JSON or values are left in place so they can be corrected instead of being silently overwritten. These numeric ceilings are a new validation policy.

embedding_device defaults to cpu. SentenceTransformer model loading and encoding run in one provider-owned child process. A backend process exit cannot terminate Hermes, and an interactive caller stops waiting after a bounded interval if the worker hangs. Set the option to auto or an explicit device such as mps, cuda, or cuda:0 after validating that backend. A non-CPU startup failure retries once on CPU; Hermes never loads the model as a fallback.

This boundary is deliberately narrow. SQLite, sqlite-vec, NumPy operations in the adapter, Cashew graph algorithms, and auxiliary LLM calls still run in the Hermes process. The child adds startup and local IPC overhead; deterministic transport checks are recorded in CI, while real-model latency and memory use remain hardware/model dependent and are not represented by those fake-model measurements.

Legacy settings

Starting with v0.11.0, the following deprecated parse-only settings are removed because cashew-brain 1.x has no supported runtime contract for them:

default_domain, auto_classify, domain_classifications, domain_separation_enabled, token_budget, walk_depth, similarity_threshold, access_weight, temporal_weight, clustering_eps, clustering_min_samples, novelty_threshold, max_think_iterations, think_cycle_nodes, gc_mode, gc_threshold, gc_grace_days, gc_protect_types, gc_think_cycle_penalty, decay_pruning, and pattern_detection.

These keys never changed provider behavior. Existing files continue to load; removed keys are ignored and pruned the next time hermes memory setup saves the provider configuration. Remove matching CASHEW_* environment variables, because they are no longer part of the adapter's configuration surface.

Feature Flags

Experimental features gated behind boolean toggles. All default to false. Enable in cashew.json under the _features key:

{"_features": {"experimental_batch_sync": true}}
Key Default Description
experimental_batch_sync false Drain up to 8 sync turns per worker iteration instead of one-at-a-time

The former experimental_parallel_retrieval setting is retired. Existing profiles still load; saving removes that setting. Recall always uses upstream retrieval first, with keyword fallback for an empty result or failure. This removes the competing retrieval threads and timing-dependent result selection; it does not impose a deadline on the normal upstream call.

Environment variables override config values: prefix any key with CASHEW_ (e.g. CASHEW_RECALL_K=10).

Verify the Install

hermes gateway restart   # ensure gateway picks up the new plugin
hermes memory status

The host can show Provider: cashew, Plugin: installed, and Status: available. Those discovery results do not prove that a turn was persisted or that semantic retrieval is ready. Availability is a cheap check of configuration/dependencies; it does not open or repair the brain.

Runtime health and concurrent sessions

The provider's read-only health_status() diagnostic distinguishes runtime readiness from discovery. It returns state, reason codes, fallback/cron status, and bounded outcome counts without querying the database or loading a model. Host versions differ in which diagnostics they display; do not interpret a successful hermes memory status command as a full integrity check.

  • Ready means initialization established the active runtime. It does not guarantee that every future extraction succeeds.
  • Degraded means a capability is unavailable. Check the reported reason and fallback: keyword recall may remain usable while semantic retrieval or writes are disabled. A missing configured auxiliary model can leave heuristic extraction available.
  • Failed, stopping, or stopped require attention to the reported lifecycle state; installed files alone do not establish an active worker.

Concurrent sessions coordinate access to the same profile through advisory leases. Contended maintenance can skip a cycle, and writer admission can time out. A lock warning is a reason to inspect competing sessions and maintenance, not to delete lock files or the SQLite journal. Stop competing processes before intentional model migration. These protections apply to participating clients; an unrelated program writing the database directly can bypass them.

sync_turn enqueues work and returns promptly; returning does not acknowledge persistence. The queue holds at most 16 pending turns. When full, the oldest pending turn is dropped with a warning to admit the newest; a remaining full queue rejects the new turn. Shutdown rejects new work and gives accepted work a bounded drain interval. The queue is in memory, so process termination can lose pending turns. Outcome diagnostics distinguish accepted work, drops, and write results; investigate warnings before assuming the conversation was stored.

Embedding model execution is isolated in a child process, but SQLite, sqlite-vec, graph operations, and auxiliary calls still run in the host process. The isolation does not make Hermes immune to every native crash or external termination. The original reported crash had no retained traceback establishing its cause; the regression tests prove specific failure boundaries, not a retrospective diagnosis.

How It Works

hermes-cashew provides two LLM-accessible tools:

  • cashew_query — searches the local thought graph for context relevant to the current conversation. Uses sqlite-vec for semantic search; optional domain, tag, and exclude_tags filters narrow the result set.
  • cashew_extract — explicitly persists a conversation turn into the graph. The agent can call this when it judges a turn contains worth-remembering knowledge.

Both tools are registered automatically when Hermes loads the plugin. On each session start, prefetch() retrieves relevant context from the graph and injects it into the system prompt.

Privacy Controls (Optional)

Nodes in the thought graph can carry tags. The cashew_query tool accepts an exclude_tags parameter to filter out nodes with specific tags from results:

{"query": "prior decisions", "exclude_tags": ["vault:private"]}

This works in both the vector search and keyword fallback paths. Common use cases:

  • Privacy: Tag sensitive nodes with vault:private to exclude them from group or shared contexts
  • Domain isolation: Exclude nodes from specific domains during broad queries
  • Declassification: Remove exclusion to reveal previously private nodes

LLM Integration

llm_aux_role selects a Hermes auxiliary role. Its default, "memory", is a selector rather than an implicit configuration: add a complete auxiliary.memory mapping to Hermes config.yaml to opt in. The plugin never creates or changes that mapping.

auxiliary:
  memory:
    provider: your-provider
    model: your-model

For each extraction, Cashew uses Hermes' public auxiliary-client resolver for the selected profile's transport and authentication, then makes one bounded chat-completions request. Prompts are capped at 32,000 characters, responses at 1,024 tokens, and the caller waits up to 30 seconds. A timed-out provider call cannot be forcibly cancelled and may finish later; no more than four such backend calls may be outstanding process-wide, and later requests fail closed until a slot is released. Cashew intentionally does not use Hermes' task-level retry and fallback ladder for these calls.

The configured role is used when Cashew next performs extraction, insight, or dream work. If Hermes cannot resolve it, Cashew silently uses its heuristic extractor instead.

To disable LLM extraction (heuristic-only mode), set llm_aux_role to null in cashew.json:

{"llm_aux_role": null}

An absent, null, or incomplete role mapping also keeps extraction heuristic-only.

What the LLM enables upstream

  • LLM extraction — structured knowledge extraction with typed nodes, confidence scores, tags, and domain assignment
  • Think cycles — cross-domain synthesis, generates insight nodes from clusters of related knowledge. Runs every think_interval sync turns (default 10). Set think_interval to 0 to disable.
  • Sleep synthesis — Graph consolidation pipeline: cross-linking, dedup, garbage collection, permanence evaluation, core memory promotion, and LLM-powered dream generation. Runs as a Hermes cron job on a configurable schedule (default: every 12 hours), not at session boundaries. The cron script reads cashew.json at runtime and uses the same explicit auxiliary role when one is configured; otherwise it runs without an LLM. Processes up to sleep_max_nodes per cycle (default 2,000).
  • Pre-compress insight extraction — Before context compression discards old messages, extracts conversation-arc patterns (topic shifts, framing changes, implicit decisions) using a dedicated LLM prompt. Creates insight/observation nodes in the graph. Requires llm_aux_role configuration. Silent-degrades without LLM.

Without an explicitly enabled llm_aux_role mapping, the plugin uses heuristic-only extraction — no API calls and no LLM cost.

Design note: The auxiliary.memory convention is provider-agnostic. Any memory provider plugin can declare llm_aux_role and reference the same auxiliary.memory section, making this a standard pattern across the Hermes plugin ecosystem.

Sleep Cycle Cron Scheduling

hermes-cashew runs its graph consolidation pipeline (cross-linking, dedup, garbage collection, permanence evaluation, core memory promotion) as a Hermes no_agent cron job, not at session boundaries. This means /new returns instantly — no synchronous sleep cycle work blocks the start of a new session.

When the cron job is registered

The cron job is created during plugin initialization (initialize()) only when all of the following are true:

Condition Config Key Default Behavior if false
Sleep cycles enabled sleep_cycles true Cron not registered
Schedule non-empty sleep_schedule \"every 12h\" Cron not registered; set to \"\" to disable
Provider init succeeds — — Exception caught, _config set to None, cron never reached
Hermes cron module available — — ImportError caught, WARNING logged
create_job() succeeds — — Exception caught, WARNING logged
Matching profile-owned job already registered — — Existing job is adopted without changing its schedule

The job persists across ordinary provider shutdown and is adopted by a later initialize for the same Hermes profile. Reconciliation serializes concurrent initializers, tags ownership with an opaque profile token, and only replaces or disables a job carrying that token. It never deletes a similarly named job that does not prove it belongs to this profile.

When the cron job runs

On the configured schedule (default every 12h), the Hermes scheduler executes $HERMES_HOME/scripts/cashew-sleep-cycle.py as a no_agent script. Registration embeds the complete validated effective configuration, including JSON/default values and any valid CASHEW_* overrides, so a cron daemon cannot drift from its provider's DB, model, device, limits, or auxiliary role. Changes to JSON or environment settings take effect on the next provider initialize, which atomically refreshes the script and reconciles the job. The cycle uses LLM dream synthesis only when that embedded configuration has an explicit configured auxiliary role; otherwise it runs with no LLM overhead.

The generated script is pinned to the Cashew installation that registered the job. This keeps one Hermes profile from loading another profile's provider. After moving, reinstalling, or changing the plugin layout, reinitialize Cashew to refresh the script and cron registration. Development installs may use the documented $HERMES_HOME/hermes-agent/plugins/memory/cashew symlink to an external checkout.

What happens during a cron tick

  1. Uses the validated effective configuration embedded when the job was registered; it does not reread cashew.json or ambient CASHEW_* values during a tick
  2. Selects up to sleep_max_nodes (default 2,000) eligible nodes
  3. Computes pairwise cosine similarity (vectorized numpy)
  4. Creates and repairs cross-links using the configured model-profile thresholds
  5. Deduplicates near-identical nodes through maximal-clique consolidation
  6. Runs garbage collection on low-fitness isolated nodes
  7. Promotes frequently-accessed nodes to permanent / core memory status
  8. Prints a JSON summary (captured by the cron scheduler's output log)

Without an explicit auxiliary role, no LLM-powered dream generation occurs in cron mode. Cross-linking, dedup, and GC still provide the graph-maintenance benefit without a provider dependency in the subprocess.

Maintenance lock scope

Embedding-dimension migration and the synchronous portion of a sleep cycle use the same nonblocking advisory lock derived from the configured cashew_db_path. A contended migration defers and a contended cycle skips; the lock file is retained because a process exit releases its flock descriptor automatically. Do not delete an old lock file to recover a cycle: unlinking it can let a second pathname refer to a different inode while the original process still holds the lock.

The Hermes adapter rejects background_dream=True with background_dream_unsupported. Supported cron runs pass false, so the adapter keeps the entire accepted cycle inside the synchronous maintenance-lock scope.

Config reference

Key Default Description
sleep_schedule \"every 12h\" Cron expression or interval string. Set to \"\" to disable cron-based scheduling entirely. Examples: \"every 30m\", \"0 */2 * * *\", \"0 3 * * *\" (daily at 3am).
sleep_max_nodes 2000 Maximum number of eligible nodes considered in one sleep cycle. Higher values can increase consolidation work and tick time.

Semantic Search

sqlite-vec enables vector similarity search and is installed automatically as a standard dependency. If your platform doesn't support sqlite-vec's native extension, the plugin degrades gracefully to keyword-based retrieval — still functional, but less precise.

sqlite-vec is a standard dependency, but loading its native extension can fail. Startup then records degraded vector availability and retains keyword + BFS retrieval.

Uninstall

Normal provider shutdown intentionally preserves the profile-owned cron job. Before removing the plugin, disable sleep in Cashew setup (set sleep_cycles to false or sleep_schedule to "") and initialize the provider once. Reconciliation then removes only the job whose ownership matches that Hermes profile. Afterwards use the host-supported removal flow:

hermes plugins remove cashew
hermes config set memory.provider built-in   # revert to built-in memory
# Irreversible: back up first, then optionally remove the local graph data.
rm -rf "${HERMES_HOME:-$HOME/.hermes}/cashew"

Troubleshooting

Plugin: NOT installed in hermes memory status

Check that Hermes installed and enabled the plugin for the active profile:

hermes plugins list
hermes plugins doctor
hermes memory status

Update or reinstall the intended plugin revision through Hermes if its files are missing. Preserve cashew.json and the graph database. Do not install an unrelated cashew-brain release to repair this plugin's bundled engine.

Status: not available

Check the active profile, declared dependency installation, and startup logs. Run scripts/verify-cashew-baseline.py with Hermes' selected plugin interpreter to verify the adjacent flat source (or add --installed to audit a wheel/sdist). A damaged or incomplete bundle requires reinstalling the same reviewed plugin revision. Model availability and initialization health are separate from dependency importability; see the health guidance above.

Hermes-agent venv has no pip

Hermes-agent creates a minimal venv without pip. Bootstrap it first:

~/.hermes/hermes-agent/venv/bin/python3 -m ensurepip
~/.hermes/hermes-agent/venv/bin/python3 -m pip install <package>

Do not run pip install from outside the venv targeting the hermes python, or the package will land in the wrong environment.

Embedding model download on first use

The plugin installs sentence-transformers. The first retrieval operation may trigger a ~500 MB embedding model download. To avoid this in automated environments:

HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 HF_DATASETS_OFFLINE=1 hermes ...

Embedding dimension migration

When embedding_model changes, the provider compares both stored embedding rows and the sqlite-vec table with the configured model's dimension during initialization. A mismatch is repaired before background workers start:

  1. a consistent SQLite backup is written under cashew/backups/;
  2. cashew-brain re-embeds active nodes and recreates vec_embeddings at the configured dimension;
  3. the provider validates the new dimensions before enabling retrieval.

If backup, migration, or validation fails, the provider logs a warning and restores the backup. Thought nodes are not discarded. Stop other Hermes or Cashew processes before deliberately changing embedding_model, then restart Hermes and allow the one-time migration to finish before issuing queries.

Development

Install uv, then use the committed lockfile for a reproducible development environment:

git clone https://github.com/magnus919/hermes-cashew
cd hermes-cashew
uv sync --frozen --extra dev
uv run --frozen --extra dev pytest

Tests require no network access and mock the embedding model automatically (HF_HUB_OFFLINE=1 is set by tests/conftest.py).

Architecture Notes

The plugin uses a dual-path loading strategy to support both pip install -e . (development) and hermes plugins install (flat-entry loader):

  • pip / test path: Python's namespace package mechanism resolves plugins.memory.cashew to plugins/memory/cashew/__init__.py via sys.path
  • flat-entry path: Hermes loads ~/.hermes/plugins/cashew/__init__.py as _hermes_user_memory.cashew. The root __init__.py detects this context and exec's the nested implementation with sys.modules patched so relative imports resolve correctly

License

See LICENSE.

About

Memory provider for Hermes Agent to work with rajkripal/cashew

Resources

Contributing

Security policy

Stars

17 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

0