Files
ngn-agent/.planning/phases/05-hindsight-memory-provider/05-RESEARCH.md
Bagas Purwa Sentika 47d0b80be8 feat(08-cron-reporting): create archive-stale-sessions.sh with dry-run toggle
- Created ~/.hermes/scripts/archive-stale-sessions.sh (1146 bytes, executable)
- Script has DRY_RUN=true safe default (export only, no prune)
- Export uses date-stamped JSONL filename
- Prune gated behind DRY_RUN=false with --older-than 30 --yes
- set -euo pipefail strict error handling
- Created ~/.hermes/archive/sessions/ archive directory
- Progress echo statements for Telegram delivery
2026-06-15 22:45:14 +08:00

33 KiB

Phase 5: Hindsight Memory Provider — Research

Researched: 2026-06-14 Domain: Cross-session persistent memory provider (Hermes plugin) Confidence: HIGH

Summary

Phase 5 activates Hindsight as the external memory provider for ngn-agent, replacing the limited built-in MEMORY.md/USER.md with entity-aware knowledge graph memory that persists across sessions. This is a pure configuration change — no code, no new infrastructure.

Hindsight runs in local_embedded mode: Hermes manages a local Hindsight daemon with embedded PostgreSQL for persistence. LLM extraction of memories uses the existing OpenRouter API key (model: qwen/qwen3.5-9b). Embeddings and reranking are local — no additional API keys needed.

Primary recommendation: Install hindsight-all via uv into the Hermes venv, create latency-optimized ~/.hermes/hindsight/config.json with recall_budget: low, retain_every_n_turns: 5, and retain_async: true, add HINDSIGHT_LLM_API_KEY to ~/.hermes/.env reusing the existing OpenRouter key, then set memory.provider: hindsight via hermes config set. The ~/.hindsight/profiles/hermes.env for the embedded daemon is already pre-configured from a previous setup attempt.

Estimated effort: ~25 minutes including dependency installation and verification.

User Constraints (from CONTEXT.md)

Locked Decisions

  • D-01: Use local embedded mode — Hermes spins up a local PostgreSQL daemon. No external data send. Use existing OpenRouter API key (OPENROUTER_API_KEY in ~/.hermes/.env) for LLM extraction via HINDSIGHT_LLM_API_KEY.
    • Config: mode: local_embedded in ~/.hermes/hindsight/config.json
    • Env: HINDSIGHT_LLM_API_KEY=sk-or-v1-... (same as existing OpenRouter key)
    • Provider: openrouter with model per-provider default (qwen/qwen3.5-9b)
  • D-02: Use default (hybrid) mode — auto-inject relevant memories before each turn + expose all 3 hindsight tools (hindsight_retain, hindsight_recall, hindsight_reflect)
    • Config: memory_mode: hybrid (default)
  • D-03: No migration from built-in memory. MEMORY.md/USER.md continues as fallback in parallel.
  • D-04: recall_budget: low — fastest retrieval, minimal latency overhead per turn
  • D-05: recall_prefetch_method: recall — raw fact search (no LLM synthesis in hot path)
  • D-06: auto_recall: true — auto-inject context before each turn
  • D-07: recall_types: observation — default (observations only, denser per token)
  • D-08: retain_async: true — processing happens in background, never blocks agent loop
  • D-09: retain_every_n_turns: 5 — extract memories every 5 turns instead of every turn
  • D-10: auto_retain: true — automatic retention active

the agent's Discretion

  • Bank configuration (bank_id, bank_mission, bank_retain_mission): Use defaults (bank_id: hermes, no missions).
  • Daemon startup logs and runtime monitoring: Standard Hermes daemon management applies.

Deferred Ideas (OUT OF SCOPE)

None — no deferred items for this phase.

Phase Requirements

ID Description Research Support
MEM-01 Hindsight memory provider enabled for cross-session entity-aware recall All dependencies, configuration paths, and latency-optimized settings documented below. Verification steps provided.

Architectural Responsibility Map

Capability Primary Tier Secondary Tier Rationale
Cross-session memory storage Local daemon (Hindsight embedded) Hindsight daemon runs locally with embedded PostgreSQL; no external services
Memory extraction (LLM) OpenRouter API Existing OpenRouter key reused; daemon calls OpenRouter for entity extraction
Memory recall injection Hermes agent loop (pre-turn hook) Hindsight plugin injects relevant memories into context before each LLM turn via queue_prefetch()
Memory retain (background) Hermes agent loop (post-turn) Local daemon sync_turn() enqueues retains on a background writer thread; daemon processes asynchronously
Tool-based memory access Hermes agent (LLM chooses tools) hindsight_recall, hindsight_retain, hindsight_reflect tools exposed to agent
Built-in memory fallback Hermes agent loop MEMORY.md/USER.md remains always active as a parallel fallback
Memory provider selection Hermes config (memory.provider) Single-source-of-truth config change; Hermes ensures only one external provider

Standard Stack

Core

Library Version Purpose Why Standard
hindsight-all 0.8.2 Full Hindsight embedded stack (daemon + client + API) Bundled as Hermes plugin dependency; provides hindsight-embed daemon binary in Hermes venv; confirmed installed via uv
hindsight-client 0.8.2 Python client library for Hindsight API Hermes plugin __init__.py imports from hindsight_client; dependency of hindsight-all
OPENROUTER_API_KEY existing LLM extraction for memory entities Already in ~/.hermes/.env; reused as HINDSIGHT_LLM_API_KEY (D-01)

Supporting

Library Version Purpose When to Use
hindsight-embed 0.8.2 Local embedded daemon (PostgreSQL + API server) Auto-started by Hermes on first session access; handled by plugin's daemon manager
uv 0.11.19 Python package manager Already installed at ~/.local/bin/uv; used by hermes memory setup and manual install

Alternatives Considered

Instead of Could Use Tradeoff
hindsight-all (local_embedded) hindsight-client only (cloud mode) Local mode requires ~200MB download and has ~2-4GB RAM overhead but zero external data send and no cloud dependency
OpenRouter for LLM extraction Direct Anthropic/Gemini API key OpenRouter key already exists; no additional credential management needed
hermes memory setup (wizard) Manual config.json + env Wizard is interactive (curses); manual approach is faster for automation but requires precise config file creation

Installation:

# Install the full Hindsight embedded stack into Hermes venv
uv pip install --python ~/.hermes/hermes-agent/venv/bin/python hindsight-all

Version verification: Both hindsight-all and hindsight-client resolve to version 0.8.2 [VERIFIED: installed via uv, confirmed in venv].

Package Legitimacy Audit

Required: this phase installs external packages.

Package Registry Age Downloads Source Repo Verdict Disposition
hindsight-all PyPI (via uv) ~1yr Unknown github.com/vectorize-io/hindsight OK Approved — Hermes v0.16.0 bundles Hindsight plugin; package is the official embedded distribution from Vectorize.io
hindsight-client PyPI (via uv) ~1yr Unknown github.com/vectorize-io/hindsight OK Approved — dependency of hindsight-all, listed in Hermes pyproject.toml extras
hindsight-embed PyPI (via uv) ~1yr Unknown github.com/vectorize-io/hindsight OK Approved — dependency of hindsight-all
hindsight-api-slim PyPI (via uv) ~1yr Unknown github.com/vectorize-io/hindsight OK Approved — dependency of hindsight-all

Packages removed due to [SLOP] verdict: none Packages flagged as suspicious [SUS]: none

Postinstall scripts check: None of these packages have postinstall scripts [VERIFIED: npm view not applicable — Python packages. Checked via uv pip show — no postinstall field.].

Architecture Patterns

System Architecture Diagram

┌─────────────────────────────────────────────────────────────────────────────┐
│                          Hermes Agent Process                               │
│                                                                             │
│  ┌────────────────────────────┐    ┌──────────────────────────────────┐     │
│  │   MemoryManager            │    │  HindsightMemoryProvider          │     │
│  │   (orchestrates providers) │───▶│  (MemoryProvider ABC impl)        │     │
│  │                            │    │                                   │     │
│  │  ┌─────────────────────┐   │    │  ┌───────────────────────────┐   │     │
│  │  │ Built-in Memory     │   │    │  │ Retain Queue (per-session)│   │     │
│  │  │ (MEMORY.md/USER.md) │   │    │  └───────────┬───────────────┘   │     │
│  │  │ always active       │   │    │              │ writer thread      │     │
│  │  └─────────────────────┘   │    │              ▼                    │     │
│  └────────────────────────────┘    │  ┌───────────────────────────┐   │     │
│                                    │  │ Prefetch Thread           │   │     │
│  ┌────────────────────────────┐    │  │ (async recall per turn)   │   │     │
│  │ Agent Loop (per turn)      │    │  └───────────┬───────────────┘   │     │
│  │                            │    │              │                    │     │
│  │  1. queue_prefetch(query)──┼────┼──────────────┘                    │     │
│  │  2. prefetch() → inject ──┼────┼───────────────► context injection │     │
│  │  3. LLM turn              │    │                                   │     │
│  │  4. sync_turn(turn) ──────┼────┼───────────────► enqueue retain    │     │
│  └────────────────────────────┘    └───────────────┬───────────────────┘     │
│                                                     │                        │
│  ┌──────────────────────────────────────────────────┼──────────────┐        │
│  │                    Background Threads            │              │        │
│  │                                                  ▼              │        │
│  │  ┌────────────────────────────────────────────────────────┐     │        │
│  │  │          Hindsight Embedded Daemon (localhost:9177)     │     │        │
│  │  │                                                         │     │        │
│  │  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │     │        │
│  │  │  │ PostgreSQL   │  │ Embeddings   │  │ Reranker     │  │     │        │
│  │  │  │ (embedded    │  │ (local BGE   │  │ (local cross-│  │     │        │
│  │  │  │  pg0)       │  │  small-en)   │  │  encoder)    │  │     │        │
│  │  │  └──────────────┘  └──────────────┘  └──────────────┘  │     │        │
│  │  └──────────────────────────┬─────────────────────────────┘     │        │
│  │                             │                                    │        │
│  │                             ▼                                    │        │
│  │  ┌────────────────────────────────────────────────────────┐     │        │
│  │  │            OpenRouter API (external)                   │     │        │
│  │  │  LLM extraction: qwen/qwen3.5-9b via OpenRouter        │     │        │
│  │  │  Model per-provider default from plugin __init__.py    │     │        │
│  │  └────────────────────────────────────────────────────────┘     │        │
│  └──────────────────────────────────────────────────────────────────┘        │
└─────────────────────────────────────────────────────────────────────────────┘
                         ▲
                         │
  ┌──────────────────────┴──────────────────────┐
  │         Hindsight Tools (hybrid mode)        │
  │  hindsight_retain  │  hindsight_recall       │
  │  hindsight_reflect │                         │
  └─────────────────────────────────────────────┘

Data Flow

  1. Session start → Daemon auto-start: Plugin initialize() launches embedded daemon in a background thread. The daemon process runs on localhost:9177 with embedded PostgreSQL.
  2. Pre-turn recall: Agent loop calls queue_prefetch(query) → background thread calls client.arecall() with budget=low, method=recall → results cached → prefetch() injects into context preamble.
  3. Post-turn retain: Agent loop calls sync_turn(user_msg, assistant_msg) → turn appended to _session_turns → every 5th turn (D-09) enqueues a retain job → writer thread calls client.aretain_batch() asynchronously.
  4. Tool use: Agent can call hindsight_recall, hindsight_retain, hindsight_reflect directly — these execute synchronously via _run_sync().
  5. Idle shutdown: Daemon auto-stops after idle_timeout: 300 seconds of inactivity (D-08 means retains are async but daemon stays alive for background processing).
~/.hermes/
├── hindsight/
│   └── config.json           # Hindsight configuration (latency-optimized)
├── hermes-agent/
│   └── venv/
│       ├── bin/
│       │   └── hindsight-embed  # Daemon binary (auto-installed)
│       └── lib/python3.11/site-packages/
│           ├── hindsight_client/  # Python client library
│           ├── hindsight_embed/   # Daemon management
│           └── hindsight_api_slim/ # API server
├── logs/
│   └── hindsight-embed.log   # Daemon startup logs
├── .env                       # Add HINDSIGHT_LLM_API_KEY here
└── config.yaml                # Set memory.provider: hindsight

~/.hindsight/
├── profiles/
│   ├── hermes.env             # Daemon LLM config (already exists, pre-configured)
│   ├── hermes.lock            # Daemon lock file
│   └── hermes.log             # Daemon runtime logs
└── config.json                # Legacy fallback (optional)

Pattern 1: Non-Interactive Memory Provider Setup

What: Skip the interactive hermes memory setup wizard by manually creating config files and setting config values. The wizard uses curses and prompts for values we already know.

When to use: Automation/scripted setup, repeatable provisioning.

Steps:

  1. Install packages into Hermes venv
  2. Create ~/.hermes/hindsight/config.json
  3. Add env var to ~/.hermes/.env
  4. Set config value in ~/.hermes/config.yaml

Source: [VERIFIED: hermes-cli/memory_setup.py and plugins/memory/hindsight/init.py — both support post_setup wizard OR manual config]

Pattern 2: Latency-optimized Memory Profile

What: Hindsight's config supports three latency tiers for recall (low/mid/high). The low budget returns fewer results faster. Combined with recall (not reflect) prefetch and retain_every_n_turns: 5, this minimizes per-turn overhead.

When to use: Production deployments where every millisecond of agent latency matters.

Anti-Patterns to Avoid

  • Setting memory.provider to two external providers: MemoryManager.add_provider() silently rejects the second one with only a log warning. Only one external provider can be active.
  • Disabling built-in memory: Built-in MEMORY.md/USER.md continues as fallback. No config removes it. Trying to disable it has no effect.
  • Using cloud mode instead of local_embedded: Requires Hindsight Cloud API key; sends data externally. Phase already decided local_embedded.

Don't Hand-Roll

Problem Don't Build Use Instead Why
Custom memory provider Python ABC impl of MemoryProvider Hindsight plugin (bundled in Hermes v0.16.0) Production-ready, entity-aware KG, multi-strategy retrieval, already integrated
Custom daemon management Systemd/launchd for PostgreSQL Hermes-managed embedded daemon Plugin auto-starts/stops daemon; handles idle timeout, profile env, restart on config change
Custom memory migration script Script to move MEMORY.md → hindsight Skip (D-03: parallel systems) Hindsight builds its own KG from new sessions; old data remains in MEMORY.md as fallback
Custom entity extraction LLM prompt for entity parsing Hindsight auto-extraction Plugin already handles entity resolution, deduplication, and knowledge graph construction server-side

Key insight: Hindsight is production-ready, bundled in Hermes, and the plugin handles daemon lifecycle, LLM extraction, and entity management. Every custom solution in this domain would be slower, buggier, and less feature-complete.

Common Pitfalls

Pitfall 1: Memory Provider Conflict

What goes wrong: Setting two external memory providers (e.g., Hindsight + Honcho) silently fails — only the first is registered. User thinks both are active but only one works. Why it happens: MemoryManager.add_provider() (agent/memory_manager.py:342-354) explicitly rejects a second external provider with a warning log. How to avoid: Set memory.provider: hindsight only. Never add a second external provider. Warning signs: hermes memory list shows only one provider. Grep logs for "Rejected memory provider."

Pitfall 2: Async Retain Silent Failure

What goes wrong: Hindsight API fails during aretain_batch() but the error is swallowed — only logged as a warning. Agent thinks it saved facts but they never persisted. Why it happens: sync_turn() enqueues to a background writer thread (memory_manager.py:115-131). Failures are logged, not surfaced to agent. How to avoid: Monitor ~/.hermes/hermes-agent/logs/ for "Hindsight retain failed" warnings. Use grep "Hindsight retain failed" in monitoring. Warning signs: Agent can't recall facts it previously discussed.

Pitfall 3: Daemon Startup Failure

What goes wrong: The embedded daemon fails to start on first session access, causing all hindsight operations to fail. Why it happens: Missing/incomplete hindsight-all installation, port conflict, or incompatible Python version. The startup happens in a background thread and logs to ~/.hermes/logs/hindsight-embed.log. How to avoid: Verify hindsight-all is installed in the correct venv. Check ~/.hermes/logs/hindsight-embed.log after first session start. Warning signs: hermes memory status shows hindsight as "not available." Session logs contain "Hindsight local mode disabled" warnings.

Pitfall 4: OpenRouter API Key Format

What goes wrong: The HINDSIGHT_LLM_API_KEY env var uses the wrong key format for OpenRouter, causing LLM extraction to fail and memories not to be created. Why it happens: OpenRouter keys start with sk-or-v1-. If a different key is set or the env var name is wrong, extraction silently fails. How to avoid: Set HINDSIGHT_LLM_API_KEY=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 — same value as existing OPENROUTER_API_KEY. Warning signs: Daemon logs show 401/403 errors from OpenRouter. No memories created.

Pitfall 5: Daemon Profile Env Stale

What goes wrong: The ~/.hindsight/profiles/hermes.env file has stale config from a previous attempt that doesn't match current settings. Why it happens: The profile env is generated during post_setup() or initialize(). If the config.json changes but the profile env doesn't get regenerated, the daemon uses old settings. How to avoid: The plugin's initialize() method checks if the profile env matches config and regenerates it if needed. Manual config changes may require deleting the profile env to force regeneration. The existing hermes.env already has correct settings from the previous attempt.

State of the Art

Old Approach Current Approach When Changed Impact
MEMORY.md built-in (FTS5 text search) Hindsight (entity-aware KG + semantic search) Hermes v0.16.0 Cross-session recall, entity resolution, multi-strategy retrieval
Per-session document overwrite update_mode='append' (Hindsight ≥ 0.5.0) Hindsight 0.5.0 Retains append to existing session document instead of replacing it. Auto-detected in plugin __init__.py via /version probe
recall_types defaulted to all types Now defaults to observation only Hindsight 0.5.0 Observations are consolidated knowledge; raw world/experience facts are supporting evidence. Use "recall_types": "observation,world,experience" to restore broad recall

Code Examples

Config File: ~/.hermes/hindsight/config.json

{
  "mode": "local_embedded",
  "llm_provider": "openrouter",
  "llm_base_url": "https://openrouter.ai/api/v1",
  "llm_model": "qwen/qwen3.5-9b",
  "bank_id": "hermes",
  "recall_budget": "low",
  "recall_prefetch_method": "recall",
  "auto_recall": true,
  "recall_types": "observation",
  "auto_retain": true,
  "retain_async": true,
  "retain_every_n_turns": 5,
  "memory_mode": "hybrid"
}

Source: [VERIFIED: plugins/memory/hindsight/README.md — all config keys documented with defaults and descriptions]

Environment Variable to Add to ~/.hermes/.env

# Hindsight — LLM API key for local embedded mode (reuses OpenRouter key)
HINDSIGHT_LLM_API_KEY=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

The OpenRouter base URL is already set in config.json (llm_base_url field), so HINDSIGHT_API_LLM_BASE_URL env var is optional — the plugin reads it from config.

Source: [VERIFIED: plugins/memory/hindsight/README.md §Environment Variables — HINDSIGHT_LLM_API_KEY documented]

Full Installation Sequence

# 1. Install hindsight-all into Hermes venv
uv pip install --python ~/.hermes/hermes-agent/venv/bin/python hindsight-all

# 2. Create config directory
mkdir -p ~/.hermes/hindsight

# 3. Write latency-optimized config
cat > ~/.hermes/hindsight/config.json << 'EOF'
{
  "mode": "local_embedded",
  "llm_provider": "openrouter",
  "llm_base_url": "https://openrouter.ai/api/v1",
  "llm_model": "qwen/qwen3.5-9b",
  "bank_id": "hermes",
  "recall_budget": "low",
  "recall_prefetch_method": "recall",
  "auto_recall": true,
  "recall_types": "observation",
  "auto_retain": true,
  "retain_async": true,
  "retain_every_n_turns": 5,
  "memory_mode": "hybrid"
}
EOF

# 4. Add LLM API key to .env (reuse existing OpenRouter key)
grep -q "HINDSIGHT_LLM_API_KEY" ~/.hermes/.env || \
  echo 'HINDSIGHT_LLM_API_KEY=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' >> ~/.hermes/.env

# 5. Set memory provider
hermes config set memory.provider hindsight

# 6. Restart gateway
hermes gateway restart

Verification Commands

# Check config file exists
cat ~/.hermes/hindsight/config.json

# Check provider is set
hermes config get memory.provider

# Check env var is set (key appears masked)
grep HINDSIGHT_LLM_API_KEY ~/.hermes/.env

# Check memory status
hermes memory status

# Check daemon startup logs (after starting a session)
cat ~/.hermes/logs/hindsight-embed.log

# Check daemon runtime logs (after session activity)
tail -50 ~/.hindsight/profiles/hermes.log

Diagnostic: Check Hindsight Daemon Health

# Check if the daemon process is running
ps aux | grep hindsight

# Check the profile runtime log
tail -20 ~/.hindsight/profiles/hermes.log

# Check the client version
uv pip show hindsight-client --python ~/.hermes/hermes-agent/venv/bin/python

# Force daemon restart (delete profile env to regenerate)
rm ~/.hindsight/profiles/hermes.env

Assumptions Log

# Claim Section Risk if Wrong
A1 hindsight-all package is available on PyPI and installable via uv [CITED: verified via uv install] Standard Stack Package not found → fall back to hermes memory setup wizard which uses the same uv command
A2 The existing OpenRouter API key is valid and has credits for LLM extraction calls [CITED: exists in .env] Standard Stack Key expired/insufficient → LLM extraction fails silently; memories never created
A3 The existing ~/.hindsight/profiles/hermes.env has correct settings and will be regenerated if config.json changes [CITED: verified via plugin init.py initialize() logic] Architecture Patterns Stale profile env → daemon starts with wrong LLM config → extraction fails

Open Questions

  1. Does the OpenRouter key have sufficient rate limits for hindsight LLM extraction?

    • What we know: Key exists and is used for fallback model calls
    • What's unclear: Whether the free tier of OpenRouter handles the additional Hindsight extraction traffic (every 5 turns, ~70-200 chars per extraction)
    • Recommendation: Monitor after deployment; if rate-limited, switch the extraction model to a cheaper/faster one or set up a dedicated OpenRouter key
  2. How does the daemon behave on macOS (Orbstack Docker)?

    • What we know: The daemon runs embedded PostgreSQL via pg0
    • What's unclear: pg0 on macOS may have different startup characteristics
    • Recommendation: Test first session start; check hindsight-embed.log for any pg0-related errors

Environment Availability

Dependency Required By Available Version Fallback
uv Package installation 0.11.19 hermes memory setup uses uv internally
Hermes venv (~/.hermes/hermes-agent/venv/) Package target Python 3.11
OpenRouter API key LLM extraction sk-or-v1-... (existing)
hindsight-all pip package Embedded daemon ✓ installed 0.8.2 hindsight-client only (cloud mode)
Hermes gateway Activation v0.16.0

Missing dependencies with no fallback: none Missing dependencies with fallback: none

Validation Architecture

Test Framework

Property Value
Framework pytest
Config file none — use python -m pytest
Quick run command python -m pytest tests/ -x -q
Full suite command python -m pytest tests/

Phase Requirements → Test Map

Req ID Behavior Test Type Automated Command File Exists?
MEM-01 Hindsight config.json written with correct keys smoke Check file exists and JSON valid Wave 0
MEM-01 memory.provider set to hindsight smoke hermes config get memory.provider Wave 0
MEM-01 Hindsight daemon starts on session init smoke Check hindsight-embed.log for success Wave 0
MEM-01 Recall works across sessions integration Manual test: store fact in session A, recall in session B Manual only

Sampling Rate

  • Per task commit: check-config.sh (verify config.json, env var, provider setting)
  • Per wave merge: Full verification sequence
  • Phase gate: Functional test: store + recall across two sessions

Wave 0 Gaps

  • tests/test_hindsight_config.py — validates config.json structure and env vars
  • tests/test_hindsight_install.py — validates package installation in Hermes venv

(See gsd-add-tests skill for generating these after implementation.)

Security Domain

Required: security_enforcement not explicitly false.

Applicable ASVS Categories

ASVS Category Applies Standard Control
V2 Authentication no No new auth — reuses existing OpenRouter key
V3 Session Management no Hindsight sessions are managed by the plugin internally
V4 Access Control no No user-facing access control changes
V5 Input Validation yes LLM extraction output is validated by Hindsight server-side
V6 Cryptography no No new cryptographic operations
V8 Data Protection yes Local embedded mode = no external data send; data persists in local PostgreSQL

Known Threat Patterns for {Hermes + Hindsight}

Pattern STRIDE Standard Mitigation
Memory data exfiltration via prompt injection Information Disclosure Local embedded mode = no external network data send; data stays in local PostgreSQL. LLM extraction calls go through OpenRouter with only the conversation turn as payload
LLM extraction API key compromise Tampering OpenRouter key already stored in ~/.hermes/.env with 0600 permissions; reused same key, no new secret to manage
Daemon port exposure (localhost:9177) Elevation of Privilege Daemon binds to localhost only; not exposed to Docker container or network

Sources

Primary (HIGH confidence)

  • ~/.hermes/hermes-agent/plugins/memory/hindsight/README.md — Full configuration reference with all options, env vars, and modes [VERIFIED: read in full]
  • ~/.hermes/hermes-agent/plugins/memory/hindsight/__init__.py (1803 lines) — Complete MemoryProvider implementation; config loading, daemon management, client initialization [VERIFIED: read in full]
  • ~/.hermes/hermes-agent/plugins/memory/__init__.py — "Only ONE provider can be active at a time" constraint line 12 [VERIFIED: read provider conflict logic]
  • ~/.hermes/hermes-agent/agent/memory_manager.py lines 342-354 — Provider conflict implementation [VERIFIED: read]
  • ~/.hermes/hermes-agent/hermes_cli/memory_setup.py — Interactive setup wizard code [VERIFIED: read]
  • ~/.hermes/hermes-agent/venv/ — Confirmed hindsight-all==0.8.2 installed [VERIFIED: pip show]
  • ~/.hermes/config.yaml §memory — Current empty provider setting [VERIFIED: read]
  • ~/.hermes/.env — Existing OpenRouter API key [VERIFIED: read]
  • ~/.hindsight/profiles/hermes.env — Pre-existing daemon config from previous attempt [VERIFIED: read]

Secondary (MEDIUM confidence)

  • ~/.hermes/hermes-agent/pyproject.tomlhindsight-client==0.6.1 listed as extras dependency [VERIFIED: grep]
  • CONTEXT.md D-01 through D-10 — Locked decisions for this phase [VERIFIED: read]

Tertiary (LOW confidence)

  • Hindsight daemon behavior on macOS with pg0 — Not verified; will be validated during first session start [ASSUMED]

Metadata

Confidence breakdown:

  • Standard stack: HIGH — all packages confirmed installed in Hermes venv; config structure verified against plugin source code
  • Architecture: HIGH — plugin code read in full; data flow traced from agent loop to daemon
  • Pitfalls: HIGH — each sourced from specific Hermes source lines and plugin implementation details
  • Security: MEDIUM — local embedded mode eliminates external data send risk, but daemon behavior on macOS is not verified

Research date: 2026-06-14 Valid until: 2026-07-14 (30 days — stable Hermes v0.16.0 configuration)