diff --git a/.planning/STATE.md b/.planning/STATE.md index 6f3f5c0..135a98d 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,15 +3,15 @@ gsd_state_version: 1.0 milestone: v1.1 milestone_name: Session Lifecycle, Memory & Reporting status: executing -stopped_at: Phase 7 context gathered -last_updated: "2026-06-15T12:13:57.820Z" -last_activity: 2026-06-15 -- Phase 06 execution started +stopped_at: Phase 7 complete — session skill created +last_updated: "2026-06-15T14:44:17.573Z" +last_activity: 2026-06-15 -- Phase 08 execution started progress: - total_phases: 4 - completed_phases: 2 - total_plans: 2 - completed_plans: 2 - percent: 50 + total_phases: 5 + completed_phases: 3 + total_plans: 5 + completed_plans: 3 + percent: 60 --- # Project State @@ -21,14 +21,14 @@ progress: See: .planning/PROJECT.md (updated 2026-06-14) **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 -Phase: 06 (default-repos-ssh-mount) — EXECUTING -Plan: 1 of 1 -Status: Executing Phase 06 -Last activity: 2026-06-15 -- Phase 06 execution started +Phase: 08 (cron-reporting) — EXECUTING +Plan: 1 of 2 +Status: Executing Phase 08 +Last activity: 2026-06-15 -- Phase 08 execution started Progress: [░░░░░░░░░░] 0% @@ -70,7 +70,7 @@ None yet. ## Session Continuity -Last session: 2026-06-15T12:13:57.813Z -Stopped at: Phase 7 context gathered -Resume file: .planning/phases/07-main-session-skill/07-CONTEXT.md +Last session: 2026-06-15T12:28:17.967Z +Stopped at: Phase 7 complete — session skill created +Resume file: .planning/phases/07-main-session-skill/07-01-SUMMARY.md Next action: /gsd-plan-phase 5 (Hindsight Memory Provider) diff --git a/.planning/phases/05-hindsight-memory-provider/05-RESEARCH.md b/.planning/phases/05-hindsight-memory-provider/05-RESEARCH.md new file mode 100644 index 0000000..53e5bb3 --- /dev/null +++ b/.planning/phases/05-hindsight-memory-provider/05-RESEARCH.md @@ -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) diff --git a/.planning/phases/08-cron-reporting/08-CONTEXT.md b/.planning/phases/08-cron-reporting/08-CONTEXT.md index ebab03e..603fe2c 100644 --- a/.planning/phases/08-cron-reporting/08-CONTEXT.md +++ b/.planning/phases/08-cron-reporting/08-CONTEXT.md @@ -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-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 -- **Report formatting**: How the Telegram message is structured (sections, bullet points) — planner can design based on Telegram's 4096-char limit -- **Cron job naming**: Standard naming convention for the two cron jobs -- **Dry-run phase**: First archive run should be manual (`hermes sessions export --older-than 30d` then review JSONL before enabling prune) +- **Report formatting**: Telegram message structure — planner designs based on 4096-char limit +- **Cron job naming**: Standard naming convention for the three cron jobs +- **Dry-run phase**: First archive via `hermes sessions export sessions.jsonl` then review JSONL before enabling prune