[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80738":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":15,"stars7d":16,"stars30d":17,"stars90d":15,"forks30d":15,"starsTrendScore":18,"compositeScore":19,"rankGlobal":9,"rankLanguage":9,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":21,"topics":23,"createdAt":9,"pushedAt":9,"updatedAt":37,"readmeContent":38,"aiSummary":39,"trendingCount":15,"starSnapshotCount":15,"syncStatus":40,"lastSyncTime":41,"discoverSource":42},80738,"scope-recall","410979729\u002Fscope-recall","410979729","Hermes scoped memory provider with SQLite truth, LanceDB semantic recall, and nightly workflow digest.",null,"Python",81,8,42,1,0,21,39,5,2.86,"MIT License",false,"main",[24,25,26,27,28,29,30,31,32,33,34,35,36],"agent","ai-agent","ai-memory","hermes","hermes-agent","hermes-plugin","hybrid-retrieval","lancedb","memory","python","scope-isolation","sqlite","vector-search","2026-06-12 02:04:06","# scope-recall\n\n\u003Cdiv align=\"center\">\n\n**Hermes current-turn memory provider with permanent semantic recall, SQLite truth storage, and a LanceDB vector companion**\n\n*Give Hermes durable memory that can follow the same user across windows\u002Fchats while keeping local scratch context from bleeding into the wrong place.*\n\nCurrent-turn recall · Permanent shared memory · Local scratch scopes · SQLite truth · LanceDB companion · Hybrid retrieval\n\n[![CI](https:\u002F\u002Fgithub.com\u002F410979729\u002Fscope-recall\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002F410979729\u002Fscope-recall\u002Factions\u002Fworkflows\u002Fci.yml)\n[![License: MIT](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-MIT-green.svg)](LICENSE)\n[![Hermes Plugin](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FHermes-Memory%20Provider-blue)](https:\u002F\u002Fhermes-agent.nousresearch.com\u002Fdocs)\n[![Python](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FPython-3.11%2B-blue)](pyproject.toml)\n[![Storage](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FStorage-SQLite%20%2B%20LanceDB-orange)](DESIGN.md)\n\n\u003C\u002Fdiv>\n\n`scope-recall` is a Hermes local memory provider built for **current-turn recall** and **permanent semantic memory**. Durable user\u002Fproject\u002Fops\u002Fmemory facts are shared across windows\u002Fchats for the same user + agent identity; raw general turn captures stay local to the current chat\u002Fthread\u002Fsession.\n\nVersion `1.0.3` is the structured-governance and embedding-credential isolation patch release for the documented V1 interfaces, packaged as a public release candidate for broader field testing. It keeps the V1 compatibility contract in [`docs\u002Fstability.md`](docs\u002Fstability.md) while adding structured classification metadata, FTS hygiene repair coverage, and a dedicated Gemini embedding credential environment variable.\n\nIt uses a **two-layer design**:\n\n- **SQLite truth store** for durable local records and deterministic auditing\n- **LanceDB vector companion** for semantic retrieval and hybrid ranking\n\nThis replaces the old `lancepro` naming, which was misleading because the earlier implementation was SQLite-only.\n\n### Design promises\n\n- **Truth stays inspectable**: SQLite remains the authoritative store; vectors are rebuildable.\n- **Recall is current-turn scoped**: retrieval is based on the active query, not stale queued context from the previous topic.\n- **Durable memory travels deliberately**: `user`, `memory`, `project`, and `ops` facts can follow the same user + agent identity across windows\u002Fchats.\n- **Scratch context stays local**: raw `general` turn captures stay inside the current chat\u002Fthread\u002Fsession boundary.\n- **Operator actions fail closed**: cross-scope export\u002Fdedupe\u002Fgovern\u002Frepair paths require explicit maintenance mode.\n- **Install remains practical**: hosted embeddings are used when configured, while deterministic `local-hash` keeps no-key bootstrap available.\n\n---\n\n## Why scope-recall?\n\nMost agent memory pain is not just \"wrong memory was recalled\". The bigger user-facing failure is often \"the agent forgot everything when I opened a new window.\" `scope-recall` therefore separates **durable facts** from **local scratch context**:\n\n- user preferences, project facts, ops notes, and explicitly stored memories follow the same user + agent identity across chats\u002Fwindows\n- raw\u002Fgeneral turn captures remain local to the current chat\u002Fthread\u002Fsession so one group's temporary chatter does not contaminate another group\n- current-turn recall searches only for memories relevant to the active query, avoiding stale previous-turn injection\n- the SQLite truth store remains auditable, and LanceDB is only a rebuildable semantic companion\n\n`scope-recall` is built around a simple rule:\n\n> Recall the relevant durable memory for the **current query**, while keeping local scratch context inside the **current runtime scope**.\n\n### Without scoped durable recall\n\n> **You:** \"For this memory-provider project, SQLite is the source of truth.\"\n>\n> *(later, in another window\u002Fchat)*\n>\n> **Agent:** \"I don't have that context here.\" ❌\n\n### With scope-recall\n\n> **You:** \"What did we decide for this Hermes memory provider?\"\n>\n> **Agent:** recalls the durable project memory from SQLite truth\u002FLanceDB companion and answers from the relevant context. ✅\n\n### Without local scratch boundaries\n\n> **Group A:** \"Temporary note: restart this group's test bot only.\"\n>\n> *(later, in Group B)*\n>\n> **Agent:** applies Group A's temporary note in Group B. ❌\n\n`scope-recall` keeps that temporary `general` scratch row local while still sharing durable `user`\u002F`memory`\u002F`project`\u002F`ops` facts.\n\n### What you get\n\n| Area | What `scope-recall` V1 provides |\n| --- | --- |\n| Current-turn recall | `prefetch(query)` retrieves against the active user query; `queue_prefetch()` is intentionally a no-op |\n| Storage authority | SQLite is the durable truth; LanceDB is rebuildable companion state |\n| Hybrid retrieval | SQLite lexical\u002FFTS candidates + LanceDB semantic candidates + bounded prompt rendering |\n| Memory scope model | shared durable scope for user\u002Fproject\u002Fops\u002Fmemory facts; local scope for general scratch captures |\n| Built-in memory integration | Hermes curated `USER.md` \u002F `MEMORY.md` are live-read, not mirrored into SQLite. In gateway contexts with an explicit `user_id`, curated-file recall is opt-in\u002Fallowlisted to avoid cross-user leakage from global profile files. |\n| Governance | deterministic exact dedupe, conservative near-duplicate merge, filtering, metadata, decay review |\n| Migration | local `lancepro` auto-migration; OpenClaw `memory-lancedb-pro` import is explicit |\n| Offline bootstrap | deterministic `local-hash` fallback when hosted embeddings are unavailable |\n\n---\n\n## Optional companion: turn-closure-audit\n\n`scope-recall` works as a standalone Hermes memory provider. You can install only this plugin and get scoped current-turn recall, SQLite truth storage, LanceDB companion retrieval, and local scratch isolation.\n\nFor stricter post-turn knowledge governance, pair it with [`turn-closure-audit`](https:\u002F\u002Fgithub.com\u002F410979729\u002Fturn-closure-audit).\n\nThe two plugins solve adjacent problems:\n\n| Plugin | Role |\n| --- | --- |\n| `scope-recall` | decides what memory should be recalled for the current turn |\n| `turn-closure-audit` | audits a completed turn and writes redacted review candidates when important knowledge may not have been retained |\n\nThis pairing is useful for long-lived Hermes agents where you want both scoped recall during a conversation and conservative review after the turn ends. It is optional, not a runtime dependency.\n\n---\n\n## Quick start\n\n### Option A: Clone into a Hermes plugin directory\n\n```bash\ncd \"$HERMES_HOME\u002Fplugins\"\ngit clone https:\u002F\u002Fgithub.com\u002F410979729\u002Fscope-recall.git scope-recall\ncd scope-recall\npython -m pip install -e .\n```\n\nThen configure Hermes to use the provider name:\n\n```yaml\nmemory:\n  provider: scope-recall\n```\n\nFor a local smoke check after installation:\n\n```bash\nhermes memory status\n```\n\n### Option B: Manual download \u002F unpacked plugin install\n\nCurrent Hermes plugin discovery expects an **unpacked plugin directory**. `scope-recall` V1 targets the current Hermes runtime line, which requires Python 3.11 or newer. If you download a release archive instead of cloning:\n\n1. unpack it as `$HERMES_HOME\u002Fplugins\u002Fscope-recall\u002F`\n2. run `python -m pip install -e \"$HERMES_HOME\u002Fplugins\u002Fscope-recall\"`\n3. set `memory.provider: scope-recall`\n4. restart\u002Freload the Hermes process that should use the provider\n5. verify with `hermes memory status`\n\nImportant boundary:\n\n- the wheel build is verified for packaging sanity and importability\n- the primary Hermes install shape for V1 is still an unpacked local plugin directory\n- do not read wheel build success as proof that Hermes can discover or install this plugin directly from a wheel alone\n\n---\n\n## Configuration\n\nThe shipped `config.json` defaults to hybrid retrieval with a hosted OpenAI-compatible Gemini embedding path and a deterministic offline fallback.\n\nMinimal default shape:\n\n```json\n{\n  \"auto_recall\": true,\n  \"auto_capture\": true,\n  \"enable_tools\": true,\n  \"maintenance_tools_enabled\": false,\n  \"retrieval\": {\n    \"mode\": \"hybrid\",\n    \"lexical_weight\": 0.45,\n    \"vector_weight\": 0.55,\n    \"candidate_pool\": 12\n  },\n  \"vector\": {\n    \"enabled\": true,\n    \"backend\": \"lancedb\",\n    \"sync_mode\": \"incremental\",\n    \"embedder\": {\n      \"provider\": \"openai-compatible\",\n      \"model\": \"gemini-embedding-001\",\n      \"dimensions\": 3072,\n      \"api_key_env\": [\"SCOPE_RECALL_GEMINI_EMBEDDING_API_KEY\"],\n      \"base_url\": \"https:\u002F\u002Fgenerativelanguage.googleapis.com\u002Fv1beta\u002Fopenai\"\n    },\n    \"fallback_embedder\": {\n      \"provider\": \"local-hash\",\n      \"dimensions\": 256,\n      \"model\": \"hash-v1\"\n    }\n  }\n}\n```\n\nCredential rule:\n\n- put real API keys in your private environment, not in `config.json`\n- if no configured key is available, `scope-recall` falls back to `local-hash`\n\n### Embedding providers\n\nCurrently implemented:\n\n| Provider | Use case | Notes |\n| --- | --- | --- |\n| `openai-compatible` | Gemini\u002FOpenAI-compatible embedding APIs | Default configured path; supports env-based API key lookup |\n| `openai` | Direct OpenAI embeddings | Useful when you do not need a custom compatible endpoint |\n| `sentence-transformers` | Local Hugging Face \u002F SentenceTransformers models | Good for local semantic embeddings when installed |\n| `local-hash` | Offline fallback | Deterministic degraded fallback, not a true semantic model |\n| `local-debug` | Tests\u002Fdebugging | Tiny deterministic test embedder |\n\nProvider aliases `local-model`, `local-embedding`, and `huggingface` resolve to the `sentence-transformers` backend.\n\n---\n\n## Durable memory vs local scratch scope\n\n`scope-recall` does **not** split all memory by every group or tiny window. It uses two provider-owned scopes:\n\n- **Shared durable scope**: `platform + agent_workspace + agent_identity + user_id`. Rows with targets `user`, `memory`, `project`, and `ops` are stored here, so they can be recalled across chats\u002Fwindows for the same user and agent.\n- **Local runtime scope**: shared durable scope plus `gateway_session_key`, or `chat_id` \u002F `thread_id`. Rows with target `general` stay here, so temporary group\u002Ftopic\u002Fsession chatter does not bleed elsewhere.\n- **Accessible scope set**: normal recall and scoped tool actions can see the current local scope plus the shared durable scope; they cannot see another user, sibling agent identity, or another local chat\u002Fthread\u002Fsession scratch scope.\n\nThis aims at the common expectation: \"if I gave the agent durable information before, it should remember it later,\" without making every scratch line globally visible forever.\n\n---\n\n## Dual-memory architecture: important\n\nWhen `scope-recall` is active, Hermes memory has **two intentional authority zones**:\n\n| Layer | Storage | Purpose | How recall sees it |\n| --- | --- | --- | --- |\n| Hermes curated memory | `$HERMES_HOME\u002Fmemories\u002FUSER.md`, `$HERMES_HOME\u002Fmemories\u002FMEMORY.md` | User profile and durable hand-curated notes managed by Hermes built-in memory | Live-read during recall; not mirrored into SQLite; gateway `user_id` contexts require curated-memory opt-in\u002Fallowlist |\n| Scope Recall provider memory | `$HERMES_HOME\u002Fscope-recall\u002Fmemory.sqlite3` + `$HERMES_HOME\u002Fscope-recall\u002Flancedb\u002F` | Provider-owned shared durable memories plus local scratch captures, scope metadata, lexical\u002Fvector retrieval | SQLite truth + optional LanceDB companion ranking |\n\nKey principle:\n\n> SQLite is the truth source for provider-owned rows. Hermes curated memory files remain their own truth source. LanceDB is a rebuildable retrieval companion, not the authority.\n\nThis is deliberate. Mirroring curated memory writes into SQLite can leave stale duplicates after replace\u002Fremove operations. Live-reading curated memory keeps Scope Recall aligned with Hermes native memory behavior. Because those curated files are profile-global, live-read recall defaults to `single-user`: it is active for single-user\u002Fno-`user_id` runtimes and disabled for explicit gateway `user_id` contexts unless `curated_memory.mode` is set to `profile-global` or `explicit-users` with matching `allowed_user_ids`.\n\n---\n\n## Storage layout\n\nUnder the active Hermes profile:\n\n- `$HERMES_HOME\u002Fscope-recall\u002Fmemory.sqlite3`\n- `$HERMES_HOME\u002Fscope-recall\u002Fconfig.json`\n- `$HERMES_HOME\u002Fscope-recall\u002Flancedb\u002F`\n\nLegacy `lancepro` storage is migrated forward on first initialization when present.\n\n---\n\n## Architecture\n\n```text\nHermes turn\n   |\n   | current query\n   v\nprefetch(query)\n   |\n   +--> live curated memory read\n   |       - $HERMES_HOME\u002Fmemories\u002FUSER.md\n   |       - $HERMES_HOME\u002Fmemories\u002FMEMORY.md\n   |\n   +--> SQLite truth lookup \u002F FTS\n   |       - provider-owned memory rows\n   |       - scope metadata\n   |       - timestamps and governance metadata\n   |\n   +--> LanceDB vector companion\n   |       - semantic candidate retrieval\n   |       - rebuildable from SQLite truth\n   |\n   v\nhybrid scoring + recency-aware ranking + bounded prompt block\n```\n\n\u003Cdetails>\n\u003Csummary>\u003Cstrong>File reference\u003C\u002Fstrong>\u003C\u002Fsummary>\n\n| File | Purpose |\n| --- | --- |\n| `__init__.py` | Hermes plugin entrypoint; exposes `register()` lazily |\n| `provider.py` | Provider lifecycle and Hermes hook integration |\n| `config.py` | Runtime config loading\u002Fdefaults |\n| `scope.py` | Runtime scope construction and isolation keys |\n| `sql_store.py` | SQLite schema, migrations, truth-row CRUD, FTS |\n| `vector_store.py` | LanceDB companion table sync\u002Fsearch\u002Frepair primitives |\n| `vector_runtime.py` | Vector runtime status and degradation handling |\n| `recall.py` | Lexical\u002Fvector\u002Fhybrid recall orchestration |\n| `scoring.py` | Score fusion, freshness boosts, capping logic |\n| `gating.py` | Recall\u002Fcapture gating and noise filtering |\n| `capture.py` | Auto-capture pipeline |\n| `governance.py` | Deterministic dedupe, metadata, decay\u002Fgovernance review |\n| `memory_ops.py` | Store\u002Fsearch\u002Fforget\u002Fupdate\u002Fdedupe\u002Fmerge\u002Fexport\u002Fgovern operations |\n| `tooling.py` | Provider tool dispatch |\n| `schemas.py` | Hermes tool schemas |\n| `migration.py` | Legacy `lancepro` migration helpers |\n| `scripts\u002Fimport.openclaw.memory_lancedb_pro.py` | Explicit OpenClaw history importer |\n| `scripts\u002Frepair.vector_index.py` | Rebuild\u002Frepair LanceDB from SQLite truth |\n| `scripts\u002Fcheck.release.py` | Full V1 release gate used locally and by CI |\n\n\u003C\u002Fdetails>\n\n### 1. SQLite truth layer\n\nSQLite is the authoritative provider-owned store.\n\nIt keeps:\n\n- raw memory rows\n- scope metadata\n- lexical FTS index\n- timestamps for auditing and migration\n\nWhy SQLite stays authoritative:\n\n- deterministic local persistence\n- easy schema inspection\n- simple migration\u002Fbackup story\n- safer open-source baseline than tying truth directly to a vector backend\n\n### 2. LanceDB vector companion\n\nLanceDB is a **companion retrieval index**, not the truth source.\n\nIt stores retrieval-ready fields copied from SQLite plus a vector column:\n\n- `id`\n- `scope_id`\n- `source`\n- `target`\n- `content`\n- `summary`\n- `updated_at`\n- `vector`\n\nConfigured default embedder targets the Gemini OpenAI-compatible embeddings API:\n\n- `provider: openai-compatible`\n- `model: gemini-embedding-001`\n- `dimensions: 3072`\n\nRuntime fallback remains available:\n\n- if the configured API embedder is unavailable, the plugin falls back to `local-hash` (`256` dims)\n- this keeps first-boot\u002Flocal operation working even without external API keys, while preserving a higher-quality default config for instances that do provide credentials\n\n---\n\n## Core features\n\n### Current-turn recall\n\n- `prefetch(query)` retrieves against the **current** user query\n- `queue_prefetch()` is intentionally a no-op\n- this avoids stale next-turn injection from the previous topic\n\n### Permanent shared recall\n\n- `user`, `memory`, `project`, and `ops` rows are durable shared memories for the same user + agent identity\n- they can be recalled from another chat\u002Fwindow when the new query is semantically relevant\n- `general` rows remain local scratch context for the current chat\u002Fthread\u002Fsession\n- ID-based updates\u002Fdeletes\u002Fmerges are restricted to the current accessible scope set, not global row ids\n\n### Hybrid retrieval\n\n```text\ncurrent query\n   ├─> SQLite lexical \u002F FTS candidates\n   └─> LanceDB vector candidates\n        ↓\nscore fusion + freshness hints + prompt budget\n```\n\nSupported retrieval modes:\n\n- `lexical`\n- `vector`\n- `hybrid` *(default)*\n\nDefault hybrid weights:\n\n- lexical: `0.45`\n- vector: `0.55`\n\nGuardrail: if only one side has a score, that side is used directly instead of being unfairly damped by a missing partner score.\n\n### Scope isolation\n\nScope is built from:\n\n- `platform`\n- `agent_workspace`\n- `agent_identity`\n- `user_id`\n- `gateway_session_key` when available\n- otherwise `chat_id`\n- plus `thread_id` when present\n\nThis prevents raw identifiers containing delimiters from colliding with split scope fields, and preserves the intended split: durable facts can move with the same user + agent identity, while local scratch rows do not leak across different groups, chats, sessions, or topics.\n\n### Vector repair and stats\n\nSQLite is the cardinality authority. During vector sync, the provider compares SQLite ids with LanceDB ids, deletes stale vector rows, collapses duplicate physical rows by id, and embeds missing\u002Fchanged rows. If LanceDB delete\u002Fupsert fails, the SQLite write is preserved and vector state becomes `needs_repair` instead of surfacing the truth-row write as failed.\n\n`scope_recall_stats` reports:\n\n- `vector.row_count` — physical LanceDB row count\n- `vector.unique_id_count` — distinct vector ids\n- `vector.duplicate_row_count` — extra physical rows beyond one row per id\n- `vector.status` — `ready`, `degraded`, `needs_repair`, `disabled`, or `error`\n\nA healthy synced companion should have `total_memories == vector.unique_id_count == vector.row_count` and `vector.duplicate_row_count == 0` for provider-owned rows.\n\nFor deeper maintenance:\n\n```bash\npython scripts\u002Frepair.vector_index.py --hermes-home \"$HERMES_HOME\" --dry-run\npython scripts\u002Frepair.vector_index.py --hermes-home \"$HERMES_HOME\"\n```\n\n### Write-time governance\n\nProvider-owned captures apply a deterministic first line of governance before SQLite writes:\n\n- exact normalized-content dedupe within `(scope_id, target)`\n- conservative semantic near-duplicate merge for `user`, `ops`, and `project` memories\n- conflict preservation when a near-duplicate contains negation \u002F supersession language\n- rules-based smart extraction from user turns into preference \u002F ops \u002F project fact candidates\n- metadata classification for category, tier, confidence, sensitivity, and expiry review\n- noisy maintenance\u002Fsystem prompt filtering\n- trivial reply filtering\n- obvious secret-bearing text filtering\n- overlong prompt-block filtering through `capture_hard_max_chars`\n- governance review through `scope_recall_govern`, including core\u002Fworking\u002Farchive tier counts and decay candidates\n\nThis is a local deterministic governance layer, not a remote LLM extraction pipeline. It intentionally stays conservative so SQLite remains auditable truth and conflicting memories are preserved rather than silently overwritten.\n\n---\n\n## Provider tools\n\nPrimary-agent default tools:\n\n```text\nscope_recall_store\nscope_recall_search\nscope_recall_forget\nscope_recall_update\nscope_recall_merge\nscope_recall_export\nscope_recall_stats\n```\n\nOperator-only maintenance tools are hidden from the default schema and require `maintenance_tools_enabled=true`:\n\n```text\nscope_recall_dedupe\nscope_recall_govern\nscope_recall_hygiene\nscope_recall_repair\n```\n\n`scope_recall_hygiene` is read-only. It reports runtime-wrapper noise, assistant scratch prose, duplicate dedupe keys, very short\u002Flong rows, `general` rows present in the vector companion, likely promotion candidates, and likely delete candidates. It does not delete, merge, promote, or rewrite rows.\n\nFor an offline SQLite report without exposing the maintenance tool to agents:\n\n```bash\npython scripts\u002Freport.hygiene.py --db \"$HERMES_HOME\u002Fscope-recall\u002Fmemory.sqlite3\" --format markdown\n```\n\nDestructive cleanup is intentionally out-of-band: use the hygiene report first, then require an explicit operator decision before running any separate delete\u002Fmerge\u002Fdedupe action. The shipped hygiene path is dry-run\u002Freport-only.\n\n`scope_recall_export` defaults to the current accessible scope set: local scratch scope plus shared durable scope. Passing `scope_only=false` is an operator maintenance action and fails closed unless `maintenance_tools_enabled=true`.\n\nBackward-compatible aliases are still accepted internally for old `lancepro_*` tool names during transition.\n\n### Tool quick reference\n\nExample primary-agent tool calls:\n\n```python\n# Store provider-owned memory. ops\u002Fuser\u002Fmemory\u002Fproject become shared durable rows; general stays local scratch.\nstore = scope_recall_store(\n    content=\"This project deploys with uv run app.\",\n    target=\"ops\",\n)\n\n# Search the current accessible scope set: local scratch plus shared durable memory.\nresults = scope_recall_search(\n    query=\"How does this project deploy?\",\n    limit=3,\n)\n\n# Inspect truth\u002Fvector health.\nstats = scope_recall_stats()\n```\n\nExample `scope_recall_stats` shape:\n\n```json\n{\n  \"provider\": \"scope-recall\",\n  \"total_memories\": 42,\n  \"scope_memories\": 7,\n  \"local_scope_memories\": 3,\n  \"shared_scope_memories\": 4,\n  \"vector\": {\n    \"enabled\": true,\n    \"ready\": true,\n    \"status\": \"ready\",\n    \"row_count\": 42,\n    \"unique_id_count\": 42,\n    \"duplicate_row_count\": 0\n  }\n}\n```\n\n| Tool | Purpose |\n| --- | --- |\n| `scope_recall_store` | Store a provider-owned memory row after deterministic governance checks |\n| `scope_recall_search` | Search the current local scratch scope plus shared durable scope with lexical\u002Fvector\u002Fhybrid retrieval |\n| `scope_recall_forget` | Delete memories matching a query within the current accessible scope set |\n| `scope_recall_update` | Replace content\u002Fcategory within the current accessible scope set; shared\u002Flocal target-mode changes are rejected |\n| `scope_recall_dedupe` | Operator-only: inspect or collapse exact duplicate rows |\n| `scope_recall_merge` | Merge same-scope memories into a target row; shared\u002Flocal mixing is rejected |\n| `scope_recall_export` | Export SQLite truth rows as JSON or JSONL; defaults to current accessible scope set |\n| `scope_recall_govern` | Operator-only: review tier distribution and decay\u002Farchive candidates |\n| `scope_recall_hygiene` | Operator-only, read-only: report memory-quality cleanup\u002Fpromotion candidates without modifying rows |\n| `scope_recall_repair` | Operator-only: repair\u002Frebuild the LanceDB companion from SQLite truth |\n| `scope_recall_stats` | Inspect storage, retrieval, scope, and vector health |\n\n---\n\n## Migration behavior\n\n### Local `lancepro` rename migration\n\nOn first boot, if `$HERMES_HOME\u002Flancepro\u002F` exists and `$HERMES_HOME\u002Fscope-recall\u002F` does not yet contain the new DB\u002Fconfig, the provider:\n\n- copies the legacy SQLite database into the new location\n- copies `config.json` forward\n- records migration info in `scope_recall_stats`\n\n### OpenClaw `memory-lancedb-pro` imports\n\nOpenClaw `memory-lancedb-pro` history is handled separately as an explicit import problem, not automatic compatibility.\n\nSee:\n\n- [`docs\u002Fmigration.md`](docs\u002Fmigration.md)\n- [`docs\u002Fdifferences-from-memory-lancedb-pro.md`](docs\u002Fdifferences-from-memory-lancedb-pro.md)\n- [`scripts\u002Fimport.openclaw.memory_lancedb_pro.py`](scripts\u002Fimport.openclaw.memory_lancedb_pro.py)\n\nDo **not** point `scope-recall` directly at an OpenClaw `.lance` directory and call it done. Old vector stores must be transformed into SQLite truth rows before the companion vector index is rebuilt.\n\n---\n\n## Compared with OpenClaw memory-lancedb-pro\n\n`scope-recall` was inspired by good public ideas in OpenClaw `memory-lancedb-pro`, especially current-turn recall, scoped memory boundaries, hybrid retrieval, and memory hygiene. It is not a line-for-line port and it does not claim feature parity.\n\n| Area | OpenClaw `memory-lancedb-pro` | `scope-recall` V1 |\n| --- | --- | --- |\n| Host agent | OpenClaw | Hermes |\n| Truth model | LanceDB-centric OpenClaw memory pipeline | SQLite truth + LanceDB companion index |\n| Recall timing | OpenClaw auto-recall hook model | Hermes `prefetch(query)` current-turn recall; no queued next-turn recall |\n| Curated memory | Separate OpenClaw markdown\u002Fjournal behavior | Hermes `USER.md` \u002F `MEMORY.md` live-read and kept authoritative |\n| Smart extraction | LLM-backed created\u002Fmerged\u002Fskipped style in upstream beta line | deterministic\u002Frules-based extraction and conservative merge |\n| Lifecycle | Weibull decay \u002F tier promotion concepts upstream | deterministic metadata classification and decay\u002Fgovernance review; no full LLM summarization tier pipeline |\n| Migration | OpenClaw-native data path | explicit importer from OpenClaw LanceDB shape into SQLite truth |\n\nHonest claim boundary:\n\n> `scope-recall` is a Hermes local memory provider for current-turn recall with SQLite truth storage, LanceDB vector companion retrieval, strong runtime scope isolation, deterministic write-time governance, and explicit migration boundaries.\n\nIt should **not** be described as:\n\n- a drop-in replacement for OpenClaw `memory-lancedb-pro`\n- a direct reuse wrapper around old `.lance` stores\n- full feature parity with upstream LLM-backed governance and lifecycle orchestration\n\n---\n\n## Troubleshooting\n\n### Recall returns stale or irrelevant context\n\nCheck that the running provider is `scope-recall`, not the deprecated `lancepro` name, and remember that live Hermes runtime freshness requires a process restart\u002Freload after code changes.\n\n```bash\nhermes memory status\n```\n\n### Vector stats show duplicate rows or missing rows\n\nRun the repair script. SQLite remains truth; the vector layer is rebuildable companion state.\n\n```bash\npython scripts\u002Frepair.vector_index.py --hermes-home \"$HERMES_HOME\" --dry-run\npython scripts\u002Frepair.vector_index.py --hermes-home \"$HERMES_HOME\"\n```\n\n### Hosted embeddings are unavailable\n\nThe provider should degrade to `local-hash`. That keeps the system usable but lowers semantic quality. Set `SCOPE_RECALL_GEMINI_EMBEDDING_API_KEY` in your private environment to use the configured hosted path.\n\n### OpenClaw `.lance` data does not appear automatically\n\nThat is expected. OpenClaw history must be explicitly imported into SQLite truth rows before the companion vector index is rebuilt.\n\n### Live gateway still behaves like the old code\n\nRelease checks prove the source tree and artifact. They do not prove a running Hermes gateway has loaded the new plugin. Restart\u002Freload the target Hermes process and verify with a real runtime smoke test before claiming live-runtime freshness.\n\n---\n\n## Current V1 limitations\n\n- vector sync is incremental by stable row id \u002F `updated_at`, with duplicate-id\u002Fstale-row repair during normal sync; `scripts\u002Frepair.vector_index.py` can rebuild the LanceDB companion from SQLite truth when deeper storage hygiene is needed\n- semantic merge is intentionally conservative and rules\u002Fscoring-based; it is not a general-purpose contradiction resolver or LLM reasoning layer\n- smart extraction is rules-based for common preference \u002F ops \u002F project-fact sentences; it is not full OpenClaw-style LLM created\u002Fmerged\u002Fskipped extraction parity\n- fallback `local-hash` is only a degraded offline path, not a true semantic model\n- old `lancepro` directory still exists as a compatibility shim during the V1 transition window\n- the supported Hermes install shape is still an unpacked plugin directory; the wheel is verified as a package artifact, not as a Hermes discovery mechanism\n\nSee [`docs\u002Fstability.md`](docs\u002Fstability.md) for the exact V1 compatibility and non-goal boundaries.\n\n---\n\n## Documentation\n\n| Document | Description |\n| --- | --- |\n| [`DESIGN.md`](DESIGN.md) | Architecture, layer split, retrieval model, migration plan, and release expectations |\n| [`docs\u002Fstability.md`](docs\u002Fstability.md) | Stable V1 compatibility contract and non-goals |\n| [`docs\u002Fmigration.md`](docs\u002Fmigration.md) | Local `lancepro` migration and explicit OpenClaw import guidance |\n| [`docs\u002Fdifferences-from-memory-lancedb-pro.md`](docs\u002Fdifferences-from-memory-lancedb-pro.md) | Honest comparison with OpenClaw `memory-lancedb-pro` |\n| [`CHANGELOG.md`](CHANGELOG.md) | Release history |\n| [`CONTRIBUTING.md`](CONTRIBUTING.md) | Contribution and development notes |\n\n---\n\n## Release and verification\n\nThe public release gate is intentionally the same script used by GitHub Actions:\n\n```bash\npython -m pytest -q\npython scripts\u002Fcheck.release.py\npython scripts\u002Frepair.vector_index.py --hermes-home \"$HERMES_HOME\" --dry-run\n```\n\n`scripts\u002Fcheck.release.py` verifies:\n\n- V1 metadata and stable public docs\n- required source files\n- full pytest suite\n- bytecode compilation\n- wheel build\n- wheel content inspection\n- temp install\u002Fimport smoke\n- obvious literal secret\u002Fprivate-path scan\n- generated artifact cleanup\n\nCurrent focused regression coverage includes:\n\n- plugin loading from `$HERMES_HOME\u002Fplugins`\n- hybrid recall returning semantically matched content\n- built-in curated memory reflection\n- vector state visible in stats\n- runtime fallback from unavailable API embeddings to `local-hash`\n- vector table rebuild when embedder dimensions change\n- vector duplicate physical rows are repaired back to one row per id\n- vector delete\u002Fupsert failure preserves SQLite truth and marks vector status `needs_repair`\n- vector search failure degrades to lexical recall and marks vector status `needs_repair`\n- write-time exact dedupe prevents repeat SQLite rows for the same normalized content in the same scope\u002Ftarget\n- length-framed scope identifiers prevent delimiter-collision between user\u002Fchat\u002Fthread\u002Fsession components\n- operator `scope_recall_dedupe(scope_only=false)` covers duplicate groups across all scopes while ordinary scoped actions remain bounded to the current accessible scope set\n- capture filtering blocks known maintenance prompts, trivial replies, obvious secret-bearing text, and overlong prompt blocks\n- semantic near-duplicate merge and conflict preservation\n- rules-based smart extraction from user turns into preference \u002F ops \u002F project fact memories\n- merge \u002F export \u002F govern provider tools\n- governance metadata classification and decay review candidates\n- provider tools cover store\u002Fsearch\u002Fforget\u002Fupdate\u002Fdedupe\u002Fmerge\u002Fexport\u002Fgovern\u002Frepair\u002Fstats\n- explicit vector companion rebuild from SQLite truth via `scripts\u002Frepair.vector_index.py`\n- `scope_recall_stats` exposes physical rows, unique ids, and duplicate-row count\n- top-level `import scope_recall` stays light without Hermes runtime modules\n- `on_memory_write` remains an intentional observational no-op\n\n---\n\n## Dependencies\n\n| Package | Purpose |\n| --- | --- |\n| `lancedb>=0.30.2` | LanceDB companion vector index |\n| `pyarrow>=24,\u003C25` | Arrow data interchange used by LanceDB |\n| `sentence-transformers` *(optional)* | Local semantic embedding models when using the `sentence-transformers` backend |\n| Hermes Agent | Host runtime and memory-provider\u002Fplugin loading |\n\n---\n\n## License\n\nMIT. See [`LICENSE`](LICENSE).\n","`scope-recall` 是一个为Hermes代理设计的当前轮次记忆提供者，具备永久语义回忆功能。它通过SQLite数据库存储持久性数据，并利用LanceDB进行向量搜索以实现混合检索，确保了强大的作用域隔离。项目的核心功能包括：跨窗口\u002F聊天会话保持用户和代理身份的记忆连续性，同时防止局部临时上下文泄露到错误的地方；使用两层设计，一层是SQLite作为权威的真实数据存储，另一层是LanceDB作为语义检索伴侣。适合需要在不同交互场景下维持一致性和准确性的人工智能代理应用场合，如客服机器人、个人助理等，能够有效提升用户体验的一致性和流畅度。",2,"2026-06-06 04:02:58","CREATED_QUERY"]