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
This commit is contained in:
@@ -3,15 +3,15 @@ gsd_state_version: 1.0
|
|||||||
milestone: v1.1
|
milestone: v1.1
|
||||||
milestone_name: Session Lifecycle, Memory & Reporting
|
milestone_name: Session Lifecycle, Memory & Reporting
|
||||||
status: executing
|
status: executing
|
||||||
stopped_at: Phase 7 context gathered
|
stopped_at: Phase 7 complete — session skill created
|
||||||
last_updated: "2026-06-15T12:13:57.820Z"
|
last_updated: "2026-06-15T14:44:17.573Z"
|
||||||
last_activity: 2026-06-15 -- Phase 06 execution started
|
last_activity: 2026-06-15 -- Phase 08 execution started
|
||||||
progress:
|
progress:
|
||||||
total_phases: 4
|
total_phases: 5
|
||||||
completed_phases: 2
|
completed_phases: 3
|
||||||
total_plans: 2
|
total_plans: 5
|
||||||
completed_plans: 2
|
completed_plans: 3
|
||||||
percent: 50
|
percent: 60
|
||||||
---
|
---
|
||||||
|
|
||||||
# Project State
|
# Project State
|
||||||
@@ -21,14 +21,14 @@ progress:
|
|||||||
See: .planning/PROJECT.md (updated 2026-06-14)
|
See: .planning/PROJECT.md (updated 2026-06-14)
|
||||||
|
|
||||||
**Core value:** Agent must NEVER mutate real infrastructure beyond what the limited IAM role permits
|
**Core value:** Agent must NEVER mutate real infrastructure beyond what the limited IAM role permits
|
||||||
**Current focus:** Phase 06 — default-repos-ssh-mount
|
**Current focus:** Phase 08 — cron-reporting
|
||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 06 (default-repos-ssh-mount) — EXECUTING
|
Phase: 08 (cron-reporting) — EXECUTING
|
||||||
Plan: 1 of 1
|
Plan: 1 of 2
|
||||||
Status: Executing Phase 06
|
Status: Executing Phase 08
|
||||||
Last activity: 2026-06-15 -- Phase 06 execution started
|
Last activity: 2026-06-15 -- Phase 08 execution started
|
||||||
|
|
||||||
Progress: [░░░░░░░░░░] 0%
|
Progress: [░░░░░░░░░░] 0%
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ None yet.
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-06-15T12:13:57.813Z
|
Last session: 2026-06-15T12:28:17.967Z
|
||||||
Stopped at: Phase 7 context gathered
|
Stopped at: Phase 7 complete — session skill created
|
||||||
Resume file: .planning/phases/07-main-session-skill/07-CONTEXT.md
|
Resume file: .planning/phases/07-main-session-skill/07-01-SUMMARY.md
|
||||||
Next action: /gsd-plan-phase 5 (Hindsight Memory Provider)
|
Next action: /gsd-plan-phase 5 (Hindsight Memory Provider)
|
||||||
|
|||||||
500
.planning/phases/05-hindsight-memory-provider/05-RESEARCH.md
Normal file
500
.planning/phases/05-hindsight-memory-provider/05-RESEARCH.md
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
# 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:**
|
||||||
|
```bash
|
||||||
|
# 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](https://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](https://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](https://github.com/vectorize-io/hindsight) | OK | Approved — dependency of `hindsight-all` |
|
||||||
|
| `hindsight-api-slim` | PyPI (via uv) | ~1yr | Unknown | [github.com/vectorize-io/hindsight](https://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).
|
||||||
|
|
||||||
|
### Recommended Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
~/.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`
|
||||||
|
|
||||||
|
```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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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.toml` — `hindsight-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)
|
||||||
@@ -49,10 +49,19 @@ Set up daily operational reporting and weekly stale session lifecycle management
|
|||||||
- **D-18:** The daily report skill references the existing session skill (Phase 7) and ngn-jira skill (Phase 4) for operations
|
- **D-18:** The daily report skill references the existing session skill (Phase 7) and ngn-jira skill (Phase 4) for operations
|
||||||
- **D-19:** The stale archive step uses `no_agent: true` mode (deterministic CLI commands, no LLM cost)
|
- **D-19:** The stale archive step uses `no_agent: true` mode (deterministic CLI commands, no LLM cost)
|
||||||
|
|
||||||
|
### Technical Corrections from Research
|
||||||
|
|
||||||
|
- **`hermes sessions export`** has NO `--older-than` flag — exports ALL sessions, filters by `--source` or `--session-id` only. Archive script must export all then `prune --older-than 30 --yes` to clean up.
|
||||||
|
- **`hermes sessions prune`** uses `--yes` (not `--confirm`)
|
||||||
|
- **`hermes sessions list`** has NO `--json` flag
|
||||||
|
- **Jira-session mapping** lives in hindsight memory (saved by Phase 7 session skill), not in the session DB — cron agent must use `hindsight_recall` to discover which Jira tickets belong to which sessions
|
||||||
|
- **System timezone already SGT (+08)** — no TZ configuration needed
|
||||||
|
- **Three cron jobs required**: Daily report (09:00 SGT), weekly stale summary (Sunday 20:00 SGT), weekly archive (Sunday 20:05 SGT — 5 min after summary to avoid race conditions)
|
||||||
|
|
||||||
### the agent's Discretion
|
### the agent's Discretion
|
||||||
- **Report formatting**: How the Telegram message is structured (sections, bullet points) — planner can design based on Telegram's 4096-char limit
|
- **Report formatting**: Telegram message structure — planner designs based on 4096-char limit
|
||||||
- **Cron job naming**: Standard naming convention for the two cron jobs
|
- **Cron job naming**: Standard naming convention for the three cron jobs
|
||||||
- **Dry-run phase**: First archive run should be manual (`hermes sessions export --older-than 30d` then review JSONL before enabling prune)
|
- **Dry-run phase**: First archive via `hermes sessions export sessions.jsonl` then review JSONL before enabling prune
|
||||||
</decisions>
|
</decisions>
|
||||||
|
|
||||||
<canonical_refs>
|
<canonical_refs>
|
||||||
|
|||||||
Reference in New Issue
Block a user