# Technical Documentation: CortexReach/memory-lancedb-pro

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/CortexReach/memory-lancedb-pro)

### Overview (`README.md`)

<div align="center">

# 🧠 memory-lancedb-pro · 🦞OpenClaw Plugin

**AI Memory Assistant for [OpenClaw](https://github.com/openclaw/openclaw) Agents**

*Give your AI agent a brain that actually remembers — across sessions, across agents, across time.*

A LanceDB-backed OpenClaw memory plugin that stores preferences, decisions, and project context, then auto-recalls them in future sessions.

[](https://github.com/openclaw/openclaw)
[](https://github.com/openclaw/openclaw)
[](https://www.npmjs.com/package/memory-lancedb-pro)
[](https://lancedb.com)
[](LICENSE)
<h2>⚡ <a href="https://github.com/CortexReach/memory-lancedb-pro/releases/tag/v1.1.0-beta.10">v1.1.0-beta.10 — OpenClaw 2026.3+ Hook Adaptation</a></h2>

<p>
 ✅ Fully adapted for OpenClaw 2026.3+ new plugin architecture<br>
 🔄 Uses <code>before_prompt_build</code> hooks (replacing deprecated <code>before_agent_start</code>)<br>
 🩺 Run <code>openclaw doctor --fix</code> after upgrading
</p>


[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JA.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Español](README_ES.md) | [Deutsch](README_DE.md) | [Italiano](README_IT.md) | [Русский](README_RU.md) | [Português (Brasil)](README_PT-BR.md)

</div>

---

## Why memory-lancedb-pro?

Most AI agents have amnesia. They forget everything the moment you start a new chat.

**memory-lancedb-pro** is a production-grade long-term memory plugin for OpenClaw that turns your agent into an **AI Memory Assistant** — it automatically captures what matters, lets noise naturally fade, and retrieves the right memory at the right time. No manual tagging, no configuration headaches.

### Your AI Memory Assistant in Action

**Without memory — every session starts from zero:**

> **You:** "Use tabs for indentation, always add error handling."
> *(next session)*
> **You:** "I already told you — tabs, not spaces!" 😤
> *(next session)*
> **You:** "...seriously, tabs. And error handling. Again."

**With memory-lancedb-pro — your agent learns and remembers:**

> **You:** "Use tabs for indentation, always add error handling."
> *(next session — agent auto-recalls your preferences)*
> **Agent:** *(silently applies tabs + error handling)* ✅
> **You:** "Why did we pick PostgreSQL over MongoDB last month?"
> **Agent:** "Based on our discussion on Feb 12, the main reasons were..." ✅

That's the difference an **AI Memory Assistant** makes — it learns your style, recalls past decisions, and delivers personalized responses without you repeating yourself.

### What else can it do?

| | What you get |
|---|---|
| **Auto-Capture** | Your agent learns from every conversation — no manual `memory_store` needed |
| **Smart Extraction** | LLM-powered 6-category classification: profiles, preferences, entities, events, cases, patterns |
| **Intelligent Forgetting** | Weibull decay model — important memories stay, noise naturally fades away |
| **Hybrid Retrieval** | Vector + BM25 full-text search, fused with cross-encoder reranking |
| **Context Injection** | Relevant memories automatically surface before each reply |
| **Multi-Scope Isolation** | Per-agent, per-user, per-project memory boundaries |
| **Any Provider** | OpenAI, Jina, Gemini, Ollama, or any OpenAI-compatible API |
| **Full Toolkit** | CLI, backup, migration, upgrade, export/import — production-ready |

---

## Quick Start

> **CPU Requirement:** Your CPU must support **AVX** instructions. LanceDB native vector search may require **AVX2** on some Linux x64 builds and can crash AVX-only CPUs with `SIGILL`; set `retrieval.disableNativeCosine: true` or `MEMORY_LANCEDB_DISABLE_NATIVE_COSINE=1` to use a scoped row scan plus JavaScript cosine ranking. Check CPU flags with: `grep -o 'avx[^ ]*' /proc/cpuinfo | head -1` (no output = not supported). See [#419](https://github.com/CortexReach/memory-lancedb-pro/issues/419) and [#644](https://github.com/CortexReach/memory-lancedb-pro/issues/644) for details.

### Option A: One-Click Install Script (Recommended)

The community-maintained **[setup script](https://github.com/CortexReach/toolbox/tree/main/memory-lancedb-pro-setup)** handles install, upgrade, and repair in one command:

```bash
curl -fsSL https://raw.githubusercontent.com/CortexReach/toolbox/main/memory-lancedb-pro-setup/setup-memory.sh -o setup-memory.sh
bash setup-memory.sh
```

> See [Ecosystem](#ecosystem) below for the full list of scenarios the script covers and other community tools.

### Option B: Manual Install

**Via OpenClaw CLI (recommended):**
```bash
openclaw plugins install memory-lancedb-pro@beta
```

**Or via npm:**
```bash
npm i memory-lancedb-pro@beta
```
> If using npm, you will also need to add the plugin's install directory as an **absolute** path in `plugins.load.paths` in your `openclaw.json`. This is the most common setup issue.

Add to your `openclaw.json`:

```json
{
  "plugins": {
    "slots": { "memory": "memory-lancedb-pro" },
    "entries": {
      "memory-lancedb-pro": {
        "enabled": true,
        "config": {
          "embedding": {
            "provider": "openai-compatible",
            "apiKey": "${OPENAI_API_KEY}",
            "model": "text-embedding-3-small"
          },
          "autoCapture": true,
          "autoRecall": true,
          "smartExtraction": true,
          "canonicalCorpus": {
            "enabled": true,
            "syncOnSearch": true
          },
          "dreaming": {
            "enabled": false
          },
          "extractMinMessages": 2,
          "extractMaxChars": 8000,
          "sessionMemory": { "enabled": false }
        }
      }
    }
  }
}
```

**Why these defaults?**
- `autoCapture` + `smartExtraction` → your agent learns from every conversation automatically
- `autoRecall` → relevant memories are injected before each reply
- `extractMinMessages: 2` → extraction triggers in normal two-turn chats
- `sessionMemory.enabled: false` → avoids polluting retrieval with session summaries on day one

---

## ⚠️ Memory Architecture (Important)

When `memory-lancedb-pro` owns the OpenClaw memory slot, it now exposes one memory capability with two coordinated stores:

| Memory Layer | Storage | What it's for | Recallable? |
|---|---|---|---|
| **Plugin Memory** | LanceDB (vector store) | Semantic recall via `memory_recall` / auto-recall | ✅ Yes |
| **Canonical Corpus** | `MEMORY.md`, `memory/**/*.md`, recent session transcripts, `memory/dreaming/**/*.md` | Source-of-truth files and public artifacts | ✅ Via LanceDB semantic index when `canonicalCorpus.enabled` is true |

**Key principle:**
> Canonical files remain the source of truth. LanceDB is the semantic index used to retrieve them with grounded paths, line spans, snippets, and citations.

**What this means for you:**
- Need semantic recall? → Use `memory_store` or let auto-capture do it
- `memory/YYYY-MM-DD.md` → treat as a **daily journal / log** that can also be indexed for semantic lookup
- `MEMORY.md` → curated human-readable reference that can be indexed as canonical context
- `memory/dreaming/**/*.md` → dream reports exposed as public artifacts and indexed as reflection context
- Session JSONL transcripts → indexed as `source: "sessions"` when `canonicalCorpus.includeSessionTranscripts` is enabled
- Plugin memory → primary write path for durable facts, preferences, decisions, and auto-captured memories

OpenClaw hosts receive:

- `api.registerMemoryCapability({ runtime, promptBuilder, flushPlanResolver, publicArtifacts })`
- runtime search results shaped as `path`, `startLine`, `endLine`, `snippet`, `source`, and `citation`
- public artifacts for `MEMORY.md`, `memory/**/*.md`, dream reports, and the dreaming event log at `memory/.dreams/events.jsonl`

Dreaming compatibility is configured under the same plugin entry. If `dreaming.enabled` is true while this plugin owns `plugins.slots.memory`, OpenClaw can use the memory-core dreaming sidecar against this plugin's memory capability and public artifacts.

On hosts exposing `openclaw/plugin-sdk/memory-recall`, interactive `memory_recall`
(including the `memory_search`/`memory_get` aliases) and successful auto-recall
injections also report surfaced canonical-file excerpts to native dreaming.
This requires canonical-corpus metadata for the current workspace, matching
content hashes, and the host's live tool/hook run context. The host validates the
excerpt against the source file, applies its existing dreaming eligibility rules,
and deduplicates repeated recalls within the same run. Only literal displayed
primary text counts; generated abstracts, neighbor text, virtual LanceDB IDs,
session transcripts, and late/failed injections do not become file evidence.
Auto-context markup transformations are conservatively excluded. Daily corpus
ingestion does not produce interactive signals. Older hosts without the API or
invocation context continue normal retrieval without this additional recording.
No native dreaming thresholds, promotion budgets, or provenance labels are
changed by this bridge; it does not write native state directly.

Validate & restart:

```bash
openclaw config validate
openclaw gateway restart
openclaw logs --follow --plain | grep "memory-lancedb-pro"
```

You should see:
- `memory-lancedb-pro: smart extraction enabled`
- `memory-lancedb-pro@...: plugin registered`

Done! Your agent now has long-term memory.

<details>
<summary><strong>More installation paths (existing users, upgrades)</strong></summary>

**Already using OpenClaw?**

1. Add the plugin with an **absolute** `plugins.load.paths` entry
2. Bind the memory slot: `plugins.slots.memory = "memory-lancedb-pro"`
3. Verify: `openclaw plugins info memory-lancedb-pro && openclaw memory-pro stats`

**Upgrading from pre-v1.1.0?**

```bash
# 1) Backup
openclaw memory-pro export --scope global --output memories-backup.json
# 2) Dry run
openclaw memory-pro upgrade --dry-run
# 3) Run upgrade
openclaw memory-pro upgrade
# 4) Verify
openclaw memory-pro stats
```

See `CHANGELOG-v1.1.0.md` for behavior changes and upgrade rationale.

**Turning on Jina task hints on an existing database?**

Task hints are two config keys under the plugin's `embedding` block:

```jsonc
"memory-lancedb-pro": {
  "config": {
    "embedding": {
      "model": "jina-embeddings-v5-text-small",
      "taskQuery": "retrieval.query",
      "taskPassage": "retrieval.passage"
    }
  }
}
```

They only affect vectors written *after* you set them. Rows already in your store
were embedded without a task hint, so a query embedded with `retrieval.query` is
compared against passages that live in a different vector space — you get roughly
half the benefit, silently.

To fix existing rows, re-embed into a **fresh database and cut over to it**. Do
**not** re-embed in place: `reembed` writes each row with `table.add()` (append by
id, not replace), so an in-place run — which is exactly what `--force` unlocks —
leaves the old vector beside the new one and **doubles every row**; retries pile on
more. `reembed` refuses same-path runs unless you force it, for this reason.

```bash
# 0) Stop the gateway so nothing writes mid-migration.

# 1) Back up the whole store — a real filesystem copy of the LanceDB directory.
#    (Do NOT rely on `memory-pro export`: it defaults to --limit 1000 and a single
#    --scope, so it silently drops rows past 1000 and every non-global scope.)
cp -r <your dbPath> <your dbPath>.bak

# 2) In the plugin config, set the task hints AND point `dbPath` at a new, empty
#    target (e.g. "<your dbPath>-v2"). A fresh target is also what lets you change
#    `embedding.dimensions`: a new vector width can only go into a new table.

# 3) Dry run — reads the source, prints the row count, writes nothing.
openclaw memory-pro reembed --source-db <old dbPath> --dry-run

# 4) Re-embed old -> new with the now-current task hints. Source != target, so each
#    id lands exactly once. Safe to re-run with --skip-existing if interrupted.
openclaw memory-pro reembed --source-db <old dbPath>

# 5) Verify before trusting the cutover: the "imported" count and
#    `memory-pro stats` on the new dbPath must equal the dry-run row count.
openclaw memory-pro stats

# 6) Start the gateway; it now opens the new dbPath.
```

The same cutover applies whenever you change `embedding.model` or
`embedding.dimensions` — always re-embed into a fresh `dbPath`, never in place.

</details>

<details>
<summary><strong>Telegram Bot Quick Import (click to expand)</strong></summary>

If you are using OpenClaw's Telegram integration, the easiest way is to send an import command directly to the main Bot instead of manually editing config.

Send this message:

```text
Help me connect this memory plugin with the most user-friendly configuration: https://github.com/CortexReach/memory-lancedb-pro

Requirements:
1. Set it as the only active memory plugin
2. Use Jina for embedding, and set embedding.taskQuery=retrieval.query and embedding.taskPassage=retrieval.passage
3. Use Jina for reranker
4. Use gpt-4o-mini for the smart-extraction LLM
5. Enable autoCapture, autoRecall, smartExtraction
6. extractMinMessages=2
7. sessionMemory.enabled=false
8. captureAssistant=false
9. retrieval mode=hybrid, vectorWeight=0.7, bm25Weight=0.3
10. rerank=cross-encoder, candidatePoolSize=12, minScore=0.6, hardMinScore=0.62
11. Generate the final openclaw.json config directly, not just an explanation
```

</details>

---

## Ecosystem

memory-lancedb-pro is the core plugin. The community has built tools around it to make setup and daily use even smoother:

### Setup Script — One-Click Install, Upgrade & Repair

> **[CortexReach/toolbox/memory-lancedb-pro-setup](https://github.com/CortexReach/toolbox/tree/main/memory-lancedb-pro-setup)**

Not just a simple installer — the script intelligently handles a wide range of real-world scenarios:

| Your situation | What the script does |
|---|---|
| Never installed | Fresh download → install deps → pick config → write to openclaw.json → restart |
| Installed via `git clone`, stuck on old commit | Auto `git fetch` + `checkout` to latest → reinstall deps → verify |
| Config has invalid fields | Auto-detect via schema filter, remove unsupported fields |
| Installed via `npm` | Skips git update, reminds you to run `npm update` yourself |
| `openclaw` CLI broken due to invalid config | Fallback: read workspace path directly from `openclaw.json` file |
| `extensions/` instead of `plugins/` | Auto-detect plugin location from config or filesystem |
| Already up to date | Run health checks only, no changes |

```bash
bash setup-memory.sh                    # Install or upgrade
bash setup-memory.sh --dry-run          # Preview only
bash setup-memory.sh --beta             # Include pre-release versions
bash setup-memory.sh --uninstall        # Revert config and remove plugin
```

Built-in provider presets: **Jina / DashScope / SiliconFlow / OpenAI / Ollama**, or bring your own OpenAI-compatible API. For full usage (including `--ref`, `--selfcheck-only`, and more), see the [setup script README](https://github.com/CortexReach/toolbox/tree/main/memory-lancedb-pro-setup).

### Claude Code / OpenClaw Skill — AI-Guided Configuration

> **[CortexReach/memory-lancedb-pro-skill](https://github.com/CortexReach/memory-lancedb-pro-skill)**

Install this skill and your AI agent (Claude Code or OpenClaw) gains deep knowledge of every feature in memory-lancedb-pro. Just say **"help me enable the best config"** and get:

- **Guided 7-step configuration workflow** with 4 deployment plans:
  - Full Power (Jina + OpenAI) / Budget (free SiliconFlow reranker) / Simple (OpenAI only) / Fully Local (Ollama, zero API cost)
- **All 9 MCP tools** used correctly: `memory_recall`, `memory_store`, `memory_forget`, `memory_update`, `memory_stats`, `memory_list`, `self_improvement_log`, `self_improvement_extract_skill`, `self_improvement_review` *(full toolset requires `enableManagementTools: true` — the default Quick Start config exposes the 4 core tools)*
- **Common pitfall avoidance**: workspace plugin enablement, `autoRecall` default-false, jiti cache, env vars, scope isolation, and more

**Install for Claude Code:**
```bash
git clone https://github.com/CortexReach/memory-lancedb-pro-skill.git ~/.claude/skills/memory-lancedb-pro
```

**Install for OpenClaw:**
```bash
git clone https://github.com/CortexReach/memory-lancedb-pro-skill.git ~/.openclaw/workspace/skills/memory-lancedb-pro-skill
```

---

## Video Tutorial

> Full walkthrough: installation, configuration, and hybrid retrieval internals.

[](https://youtu.be/MtukF1C8epQ)
**https://youtu.be/MtukF1C8epQ**

[](https://www.bilibili.com/video/BV1zUf2BGEgn/)
**https://www.bilibili.com/video/BV1zUf2BGEgn/**

---

## Architecture

```
┌─────────────────────────────────────────────────────────┐
│                   index.ts (Entry Point)                │
│  Plugin Registration · Config Parsing · Lifecycle Hooks │
└────────┬──────────┬──────────┬──────────┬───────────────┘
         │          │          │          │
    ┌────▼───┐ ┌────▼───┐ ┌───▼────┐ ┌──▼──────────┐
    │ store  │ │embedder│ │retriever│ │   scopes    │
    │ .ts    │ │ .ts    │ │ .ts    │ │    .ts      │
    └────────┘ └────────┘ └────────┘ └─────────────┘
         │                     │
    ┌────▼───┐           ┌─────▼──────────┐
    │migrate │           │noise-filter.ts │
    │ .ts    │           │adaptive-       │
    └────────┘           │retrieval.ts    │
                         └────────────────┘
    ┌─────────────┐   ┌──────────┐
    │  tools.ts   │   │  cli.ts  │
    │ (Agent API) │   │ (CLI)    │
    └─────────────┘   └──────────┘
```

> For a deep-dive into the full architecture, see [docs/memory_architecture_analysis.md](docs/memory_architecture_analysis.md).

<details>
<summary><strong>File Reference (click to expand)</strong></summary>

| File | Purpose |
| --- | --- |
| `index.ts` | Plugin entry point. Registers with OpenClaw Plugin API, parses config, mounts lifecycle hooks via `api.on()` and command hooks via `api.registerHook()` |
| `openclaw.plugin.json` | Plugin metadata + full JSON Schema config declaration |
| `cli.ts` | CLI commands: `memory-pro list/search/stats/delete/delete-bulk/export/import/reembed/upgrade/migrate` |
| `src/store.ts` | LanceDB storage layer. Table creation / FTS indexing / Vector search / BM25 search / CRUD |
| `src/embedder.ts` | Embedding abstraction. Compatible with any OpenAI-compatible API provider |
| `src/retriever.ts` | Hybrid retrieval engine. Vector + BM25 → Hybrid Fusion → Rerank → Lifecycle Decay → Filter |
| `src/scopes.ts` | Multi-scope access control |
| `src/tools.ts` | Agent tool definitions: `memory_recall`, `memory_store`, `memory_forget`, `memory_update` + management tools |
| `src/noise-filter.ts` | Filters out agent refusals, meta-questions, greetings, and low-quality content |
| `src/adaptive-retrieval.ts` | Determines whether a query needs memory retrieval |
| `src/migrate.ts` | Migration from built-in `memory-lancedb` to Pro |
| `src/smart-extractor.ts` | LLM-powered 6-category extraction with L0/L1/L2 layered storage and two-stage dedup |
| `src/decay-engine.ts` | Weibull stretched-exponential decay model |
| `src/tier-manager.ts` | Three-tier promotion/demotion: Peripheral ↔ Working ↔ Core |

</details>

---

## Core Features

### Hybrid Retrieval

```
Query → embedQuery() ─┐
                       ├─→ Hybrid Fusion → Rerank → Lifecycle Decay Boost → Length Norm → Filter
Query → BM25 FTS ─────┘
```

- **Vector Search** — semantic similarity via LanceDB ANN (cosine distance)
- **BM25 Full-Text Search** — exact keyword matching via LanceDB FTS index
- **Hybrid Fusion** — vector score as base, BM25 hits receive a weighted boost (not standard RRF — tuned for real-world recall quality)
- **Configurable Weights** — `vectorWeight`, `bm25Weight`, `minScore`

### Cross-Encoder Reranking

- Built-in adapters for **Jina**, **SiliconFlow**, **Voyage AI**, and **Pinecone**
- Compatible with any Jina-compatible endpoint (e.g., Hugging Face TEI, DashScope)
- Hybrid scoring: 60% cross-encoder + 40% original fused score
- Graceful degradation: falls back to cosine similarity on API failure

### Multi-Stage Scoring Pipeline

| Stage | Effect |
| --- | --- |
| **Hybrid Fusion** | Combines semantic and exact-match recall |
| **Cross-Encoder Rerank** | Promotes semantically precise hits |
| **Lifecycle Decay Boost** | Weibull freshness + access frequency + importance × confidence |
| **Length Normalization** | Prevents long entries from dominating (anchor: 500 chars) |
| **Hard Min Score** | Removes irrelevant results (default: 0.35) |
| **MMR Diversity** | Cosine similarity > 0.85 → demoted |

### Smart Memory Extraction (v1.1.0)

- **LLM-Powered 6-Category Extraction**: profile, preferences, entities, events, cases, patterns
- **L0/L1/L2 Layered Storage**: L0 (one-sentence index) → L1 (structured summary) → L2 (full narrative)
- **Two-Stage Dedup**: vector similarity pre-filter (≥0.7) → LLM semantic decision (CREATE/MERGE/SKIP)
- **Category-Aware Merge**: `profile` always merges, `events`/`cases` are append-only

### Memory Lifecycle Management (v1.1.0)

- **Weibull Decay Engine**: composite score = recency + frequency + intrinsic value
- **Three-Tier Promotion**: `Peripheral ↔ Working ↔ Core` with configurable thresholds
- **Access Reinforcement**: frequently recalled memories decay slower (spaced-repetition style)
- **Importance-Modulated Half-Life**: important memories decay slower

### Multi-Scope Isolation

- Built-in scopes: `global`, `agent:<id>`, `custom:<name>`, `project:<id>`, `user:<id>`
- Agent-level access control via `scopes.agentAccess`
- Default: each agent accesses `global` + its own `agent:<id>` scope
- Read-only tools such as `memory_recall`, `memory_search`, `memory_list`, and `memory_debug` fail soft when a requested scope is inaccessible: they search the caller's accessible scopes instead and return `ignoredScope` plus `accessibleScopes` in details. Write and mutation tools still return `scope_access_denied` for inaccessible scopes.

### Auto-Capture & Auto-Recall

- **Auto-Capture** (`agent_end`): extracts preference/fact/decision/entity from conversations, deduplicates, stores up to 3 per turn
- **Auto-Recall** (`before_prompt_build`): injects `<relevant-memories>` context (up to 3 entries)

> **Note (v1.1.0-beta.9+):** Auto-recall now uses the `before_prompt_build` hook instead of the deprecated `before_agent_start`. See [Hook Adaptation](#hook-adaptation-openclaw-20263) below for details.

### Noise Filtering & Adaptive Retrieval

- Filters low-quality content: agent refusals, meta-questions, greetings
- Skips retrieval for greetings, slash commands, simple confirmations, emoji
- Forces retrieval for memory keywords ("remember", "previously", "last time")
- CJK-aware thresholds (Chinese: 6 chars vs English: 15 chars)

---

<details>
<summary><strong>Compared to Built-in <code>memory-lancedb</code> (click to expand)</strong></summary>

| Feature | Built-in `memory-lancedb` | **memory-lancedb-pro** |
| --- | :---: | :---: |
| Vector search | Yes | Yes |
| BM25 full-text search | - | Yes |
| Hybrid fusion (Vector + BM25) | - | Yes |
| Cross-encoder rerank (multi-provider) | - | Yes |
| Recency boost & time decay | - | Yes |
| Length normalization | - | Yes |
| MMR diversity | - | Yes |
| Multi-scope isolation | - | Yes |
| Noise filtering | - | Yes |
| Adaptive retrieval | - | Yes |
| Management CLI | - | Yes |
| Session memory | - | Yes |
| Task-aware embeddings | - | Yes |
| **LLM Smart Extraction (6-category)** | - | Yes (v1.1.0) |
| **Weibull Decay + Tier Promotion** | - | Yes (v1.1.0) |
| Any OpenAI-compatible embedding | Limited | Yes |

</details>

---

## Configuration

API-key fields (`embedding.apiKey`, `retrieval.rerankApiKey`, and `llm.apiKey`) accept plain strings, `${ENV_VAR}` placeholders, or SecretRef objects. This plugin supports the `env` and `file` SecretRef sources:

```json
{
  "embedding": {
    "apiKey": { "source": "env", "id": "JINA_API_KEY" }
  },
  "retrieval": {
    "rerankApiKey": { "source": "file", "id": "~/.openclaw/secrets/jina-rerank" }
  }
}
```

For `source: "file"`, `id` is resolved through OpenClaw's `api.resolvePath()` and then read as a UTF-8 file. The optional `provider` field is accepted for SecretRef object-shape compatibility but is not used for provider dispatch. `exec` and other SecretRef sources are rejected by runtime config validation.

<details>
<summary><strong>Full Configuration Example</strong></summary>

```
/* Detailed source-code truncated for AI context efficiency. */
```

</details>

### OpenClaw Hook Permissions

The published plugin manifest declares `hooks.allowConversationAccess` so the `agent_end` auto-capture hook can read conversation context. If you maintain a custom plugin entry or non-bundled install that overrides manifest hook metadata, keep conversation access enabled:

```json
{
  "plugins": {
    "entries": {
      "memory-lancedb-pro": {
        "hooks": {
          "allowConversationAccess": true
        }
      }
    }
  }
}
```

Without conversation access, recall can still load but auto-capture may be blocked.

<details>
<summary><strong>Embedding Providers</strong></summary>

Works with **OpenAI-compatible embedding APIs**, including provider-specific payload adapters for services such as Jina and Voyage:

| Provider | Model | Base URL | Dimensions |
| --- | --- | --- | --- |
| **Jina** (recommended) | `jina-embeddings-v5-text-small` | `https://api.jina.ai/v1` | 1024 |
| **OpenAI** | `text-embedding-3-small` | `https://api.openai.com/v1` | 1536 |
| **Voyage** | `voyage-4-lite` / `voyage-4` | `https://api.voyageai.com/v1` | 1024 / 1024 |
| **Google Gemini** | `gemini-embedding-001` | `https://generativelanguage.googleapis.com/v1beta/openai/` | 3072 |
| **Ollama** (local) | `nomic-embed-text` | `http://localhost:11434/v1` | provider-specific |

Voyage embedding requests use Voyage's `model` + `input` payload shape. When `requestDimensions` is configured it is sent as `output_dimension`; OpenAI-only fields such as `encoding_format` are omitted.

Set `embedding.maxInputChars` for local embedding servers with small context or batch limits. The plugin applies a conservative default for `nomic-embed-text`; with automatic chunking enabled, longer documents are split before the cap is applied to each provider request.

</details>

<details>
<summary><strong>Rerank Providers</strong></summary>

Cross-encoder reranking supports multiple providers via `rerankProvider`:

| Provider | `rerankProvider` | Example Model |
| --- | --- | --- |
| **Jina** (default) | `jina` | `jina-reranker-v3` |
| **SiliconFlow** (free tier available) | `siliconflow` | `BAAI/bge-reranker-v2-m3` |
| **Voyage AI** | `voyage` | `rerank-2.5` |
| **Pinecone** | `pinecone` | `bge-reranker-v2-m3` |

Any Jina-compatible rerank endpoint also works — set `rerankProvider: "jina"` and point `rerankEndpoint` to your service (e.g., Hugging Face TEI, DashScope `qwen3-rerank`).

</details>

<details>
<summary><strong>Smart Extraction (LLM) — v1.1.0</strong></summary>

When `smartExtraction` is enabled (default: `true`), the plugin uses an LLM to intelligently extract and classify memories instead of regex-based triggers.

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `smartExtraction` | boolean | `true` | Enable/disable LLM-powered 6-category extraction |
| `llm.auth` | string | `api-key` | `api-key` uses `llm.apiKey` / `embedding.apiKey`; `oauth` uses a plugin-scoped OAuth token file by default |
| `llm.apiKey` | string | *(falls back to `embedding.apiKey`)* | API key for the LLM provider |
| `llm.model` | string | `openai/gpt-oss-120b` | LLM model name |
| `llm.baseURL` | string | *(falls back to `embedding.baseURL`)* | LLM API endpoint |
| `llm.oauthProvider` | string | `openai-codex` | OAuth provider id used when `llm.auth` is `oauth` |
| `llm.oauthPath` | string | `~/.openclaw/.memory-lancedb-pro/oauth.json` | OAuth token file used when `llm.auth` is `oauth` |
| `llm.timeoutMs` | number | `30000` | LLM request timeout in milliseconds |
| `extractMinMessages` | number | `2` | Minimum messages before extraction triggers |
| `extractMaxChars` | number | `8000` | Maximum characters sent to the LLM |


OAuth `llm` config (use existing Codex / ChatGPT login cache for LLM calls):
```json
{
  "llm": {
    "auth": "oauth",
    "oauthProvider": "openai-codex",
    "model": "gpt-5.4",
    "oauthPath": "${HOME}/.openclaw/.memory-lancedb-pro/oauth.json",
    "timeoutMs": 30000
  }
}
```

Notes for `llm.auth: "oauth"`:

- `llm.oauthProvider` is currently `openai-codex`.
- OAuth tokens default to `~/.openclaw/.memory-lancedb-pro/oauth.json`.
- You can set `llm.oauthPath` if you want to store that file somewhere else.
- `auth login` snapshots the previous api-key `llm` config next to the OAuth file, and `auth logout` restores that snapshot when available.
- Switching from `api-key` to `oauth` does not automatically carry over `llm.baseURL`. Set it manually in OAuth mode only when you intentionally want a custom ChatGPT/Codex-compatible backend.

</details>

<details>
<summary><strong>Legacy CPU Fallback</strong></summary>

If an AVX-only Linux x64 host crashes inside LanceDB native vector search with `SIGILL`, disable native cosine and let memory-lancedb-pro scan scoped rows and rank them in JavaScript:

```json
{
  "retrieval": {
    "disableNativeCosine": true
  }
}
```

You can also set `MEMORY_LANCEDB_DISABLE_NATIVE_COSINE=1` for the same behavior.

</details>

<details>
<summary><strong>Lifecycle Configuration (Decay + Tier)</strong></summary>

| Field | Default | Description |
|-------|---------|-------------|
| `decay.recencyHalfLifeDays` | `30` | Base half-life for Weibull recency decay |
| `decay.frequencyWeight` | `0.3` | Weight of access frequency in composite score |
| `decay.intrinsicWeight` | `0.3` | Weight of `importance × confidence` |
| `decay.betaCore` | `0.8` | Weibull beta for `core` memories |
| `decay.betaWorking` | `1.0` | Weibull beta for `working` memories |
| `decay.betaPeripheral` | `1.3` | Weibull beta for `peripheral` memories |
| `tier.coreAccessThreshold` | `10` | Min recall count before promoting to `core` |
| `tier.peripheralAgeDays` | `60` | Age threshold for demoting stale memories |

</details>

<details>
<summary><strong>Access Reinforcement</strong></summary>

Frequently recalled memories decay more slowly (spaced-repetition style).

Config keys (under `retrieval`):
- `reinforcementFactor` (0-2, default: `0.5`) — set `0` to disable
- `maxHalfLifeMultiplier` (1-10, default: `3`) — hard cap on effective half-life

</details>

---

## CLI Commands

```bash
openclaw memory-pro list [--scope global] [--category fact] [--limit 20] [--json]
openclaw memory-pro search "query" [--scope global] [--limit 10] [--json]
openclaw memory-pro stats [--scope global] [--json]
openclaw memory-pro auth login [--provider openai-codex] [--model gpt-5.4] [--oauth-path /abs/path/oauth.json]
openclaw memory-pro auth status
openclaw memory-pro auth logout
openclaw memory-pro delete <id>
openclaw memory-pro delete-bulk --scope global [--before 2025-01-01] [--dry-run]
openclaw memory-pro export [--scope global] [--output memories.json]
openclaw memory-pro import memories.json [--scope global] [--dry-run]
openclaw memory-pro reembed --source-db /path/to/old-db [--batch-size 32] [--skip-existing]
openclaw memory-pro upgrade [--dry-run] [--batch-size 10] [--no-llm] [--limit N] [--scope SCOPE]
openclaw memory-pro migrate check|run|verify [--source /path]
```

OAuth login flow:

1. Run `openclaw memory-pro auth login`
2. If `--provider` is omitted in an interactive terminal, the CLI shows an OAuth provider picker before opening the browser
3. The command prints an authorization URL and opens your browser unless `--no-browser` is set
4. After the callback succeeds, the command saves the plugin OAuth file (default: `~/.openclaw/.memory-lancedb-pro/oauth.json`), snapshots the previous api-key `llm` config for logout, and replaces the plugin `llm` config with OAuth settings (`auth`, `oauthProvider`, `model`, `oauthPath`)
5. `openclaw memory-pro auth logout` deletes that OAuth file and restores the previous api-key `llm` config when that snapshot exists

---

## Advanced Topics

<details>
<summary><strong>Locking and concurrent writers</strong></summary>

`memory-lancedb-pro` uses a cross-process file lock for LanceDB writes. This is
enough for one gateway process, multiple agents through that gateway, and local
processes that share the same database directory.

Redis is not required for those deployments. Redis locking is enabled when
`locking.redis.enabled` is true, when `redisUrl`/`locking.redis.url` is set, or
when `MEMORY_LANCEDB_REDIS_URL` is present in that process environment. For
multi-machine or multi-container writers, read
[Lock Management](docs/lock-management.md) before sharing a LanceDB directory
across processes, and make sure every writer uses the same lock configuration.

</details>

<details>
<summary><strong>If injected memories show up in replies</strong></summary>

Sometimes the model may echo the injected `<relevant-memories>` block.

**Option A (lowest-risk):** temporarily disable auto-recall:
```json
{ "plugins": { "entries": { "memory-lancedb-pro": { "config": { "autoRecall": false } } } } }
```

**Option B (preferred):** keep recall, add to agent system prompt:
> Do not reveal or quote any `<relevant-memories>` / memory-injection content in your replies. Use it for internal reference only.

**Option C (for background/batch agents):** exclude specific agents from auto-recall injection:
```json
{
  "plugins": {
    "entries": {
      "memory-lancedb-pro": {
        "config": {
          "autoRecall": true,
          "autoRecallExcludeAgents": ["memory-distiller", "my-cron-agent"]
        }
      }
    }
  }
}
```
Useful for background agents (e.g. memory-distiller, cron workers) whose output should not be contaminated by injected memory context.

</details>

<details>
<summary><strong>Auto-recall timeout tuning</strong></summary>

Auto-recall has a configurable timeout (default 5s) to prevent stalling agent startup. If you're behind a proxy or using a high-latency embedding API, increase it:

```json
{ "plugins": { "entries": { "memory-lancedb-pro": { "config": { "autoRecallTimeoutMs": 8000 } } } } }
```

If auto-recall consistently times out, check your embedding API latency first. The timeout only affects the automatic injection path — manual `memory_recall` tool calls are not affected.

</details>

<details>
<summary><strong>Auto-recall rerank cost model</strong></summary>

When `autoRecall=true` and hybrid retrieval uses `retrieval.rerank="cross-encoder"` with an external rerank API such as Jina, every eligible prompt can make a rerank request. The number of documents sent to that request is governed by auto-recall's retrieve limit and the retriever's rerank input window, not directly by `retrieval.candidatePoolSize` or by the final `autoRecallMaxItems` injection cap.

For example, with `autoRecallMaxItems: 3`, auto-recall asks retrieval for 6 items and hybrid retrieval may send up to 12 candidates to the external reranker before injecting at most 3 memories. To reduce external rerank usage, lower `autoRecallMaxItems` or `maxRecallPerTurn`, switch `retrieval.rerank` to `"lightweight"` or `"none"`, increase `autoRecallMinLength`, or keep auto-recall disabled and use manual `memory_recall` where appropriate.

Startup logs warn when auto-recall plus hybrid cross-encoder rerank can send more items to the reranker than it will inject. Debug auto-recall stats include the actual `rerankInput`, `rerankInputLimit`, `retrieveLimit`, `rerank`, `rerankProvider`, and configured `retrievalCandidatePoolSize`.

</details>

<details>
<summary><strong>Session Memory</strong></summary>

- Triggered on `/new` command — saves previous session summary to LanceDB
- Disabled by default (OpenClaw already has native `.jsonl` session persistence)
- Configurable message count (default: 15)

See [docs/openclaw-integration-playbook.md](docs/openclaw-integration-playbook.md) for deployment modes and `/new` verification.

</details>

<details>
<summary><strong>Custom Slash Commands (e.g. /lesson)</strong></summary>

Add to your `CLAUDE.md`, `AGENTS.md`, or system prompt:

```markdown
## /lesson command
When the user sends `/lesson <content>`:
1. Use memory_store to save as category=fact (raw knowledge)
2. Use memory_store to save as category=decision (actionable takeaway)
3. Confirm what was saved

## /remember command
When the user sends `/remember <content>`:
1. Use memory_store to save with appropriate category and importance
2. Confirm with the stored memory ID
```

</details>

<details>
<summary><strong>Iron Rules for AI Agents</strong></summary>

> Copy the block below into your `AGENTS.md` so your agent enforces these rules automatically.

```markdown
## Rule 1 — Dual-layer memory storage
Every pitfall/lesson learned → IMMEDIATELY store TWO memories:
- Technical layer: Pitfall: [symptom]. Cause: [root cause]. Fix: [solution]. Prevention: [how to avoid]
  (category: fact, importance >= 0.8)
- Principle layer: Decision principle ([tag]): [behavioral rule]. Trigger: [when]. Action: [what to do]
  (category: decision, importance >= 0.85)

## Rule 2 — LanceDB hygiene
Entries must be short and atomic (< 500 chars). No raw conversation summaries or duplicates.

## Rule 3 — Recall before retry
On ANY tool failure, ALWAYS memory_recall with relevant keywords BEFORE retrying.

## Rule 4 — Confirm target codebase
Confirm you are editing memory-lancedb-pro vs built-in memory-lancedb before changes.

## Rule 5 — Clear jiti cache after plugin code changes
After modifying .ts files under plugins/, MUST run rm -rf /tmp/jiti/ BEFORE openclaw gateway restart.
```

</details>

<details>
<summary><strong>Database Schema</strong></summary>

LanceDB table `memories`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | string (UUID) | Primary key |
| `text` | string | Memory text (FTS indexed) |
| `vector` | float[] | Embedding vector |
| `category` | string | Storage category: `preference` / `fact` / `decision` / `entity` / `reflection` / `other` |
| `scope` | string | Scope identifier (e.g., `global`, `agent:main`) |
| `importance` | float | Importance score 0-1 |
| `timestamp` | int64 | Creation timestamp (ms) |
| `metadata` | string (JSON) | Extended metadata |

Common `metadata` keys in v1.1.0: `l0_abstract`, `l1_overview`, `l2_content`, `memory_category`, `tier`, `access_count`, `confidence`, `last_accessed_at`

> **Note on categories:** The top-level `category` field uses 6 storage categories. The 6-category semantic labels from Smart Extraction (`profile` / `preferences` / `entities` / `events` / `cases` / `patterns`) are stored in `metadata.memory_category`.

</details>

<details>
<summary><strong>Troubleshooting</strong></summary>

### "Cannot mix BigInt and other types" (LanceDB / Apache Arrow)

On LanceDB 0.26+, some numeric columns may be returned as `BigInt`. Upgrade to **memory-lancedb-pro >= 1.0.14** — this plugin now coerces values using `Number(...)` before arithmetic.

</details>

---

## Hook Adaptation (OpenClaw 2026.3+)

Starting with v1.1.0-beta.9, the plugin's lifecycle hooks have been updated for compatibility with the refactored OpenClaw plugin system.

### What changed

| Hook | Before | After | Why |
|------|--------|-------|-----|
| Auto-recall | `before_agent_start` | `before_prompt_build` (priority 10) | `before_agent_start` is deprecated; `before_prompt_build` is the recommended hook for prompt mutation |
| Reflection invariants | `before_agent_start` | `before_prompt_build` (priority 12) | Same reason as above |
| Reflection derived focus | `before_prompt_build` | `before_prompt_build` (priority 15) | Unchanged event, added explicit priority |
| All other lifecycle hooks | unchanged | unchanged | `agent_end`, `after_tool_call`, `session_end`, `message_received`, `before_message_write` |

### Hook API distinction

OpenClaw exposes two hook registration methods. They write to **different registries**:

| Method | Registry | Dispatch | Use for |
|--------|----------|----------|---------|
| `api.on(event, handler, opts)` | `registry.typedHooks` | Dispatched by the lifecycle hook runner | Lifecycle events: `before_prompt_build`, `agent_end`, `after_tool_call`, `session_end`, `message_received`, `before_message_write` |
| `api.registerHook(event, handler, opts)` | `registry.hooks` | Dispatched by the internal hook system | Command/bootstrap events: `command:new`, `command:reset`, `agent:bootstrap` |

Using the wrong method causes hooks to register silently without firing. This plugin uses `api.on()` for all lifecycle hooks and `api.registerHook()` for command hooks.

### Verifying hooks after install

```bash
openclaw plugins info memory-lancedb-pro
```

You should see:

```
Legacy before_agent_start: no

Typed hooks:
  agent_end
  before_message_write
  before_prompt_build (priority 10)
  message_received

Custom hooks:
  memory-lancedb-pro-session-memory: command:new
```

If `Legacy before_agent_start: yes` appears, you are running an older version of the plugin.

### Migration from older versions

If you are upgrading from v1.1.0-beta.8 or earlier:

1. Replace the plugin files (copy or `openclaw plugins install`)
2. Clear the jiti cache: `rm -rf /tmp/jiti/`
3. Restart the gateway: `openclaw gateway restart`
4. Verify: `openclaw plugins info memory-lancedb-pro` should show `Legacy before_agent_start: no`

No config changes or data migration required. All existing memories, scopes, and settings are preserved.

### OpenClaw version requirements

- **Minimum:** OpenClaw 2026.3.22
- **Recommended:** OpenClaw latest (2026.3.23+)

This version uses `before_prompt_build` hooks (replacing the deprecated `before_agent_start`), which requires OpenClaw 2026.3.22 or later. Running `openclaw doctor --fix` after upgrading will automatically migrate plugin config (e.g. `minimax-portal-auth` → `minimax`, Brave search as a standalone plugin).

To upgrade OpenClaw:

```bash
npm update -g openclaw
openclaw --version    # verify >= 2026.3.22
openclaw doctor --fix # resolve any stale config after upgrade
```

---

## Documentation

| Document | Description |
| --- | --- |
| [OpenClaw Integration Playbook](docs/openclaw-integration-playbook.md) | Deployment modes, verification, regression matrix |
| [Memory Architecture Analysis](docs/memory_architecture_analysis.md) | Full architecture deep-dive |
| [CHANGELOG v1.1.0](docs/CHANGELOG-v1.1.0.md) | v1.1.0 behavior changes and upgrade rationale |
| [Release Checklist](docs/release-checklist.md) | Package preflight, publish dry run, and post-publish smoke checks |
| [Long-Context Chunking](docs/long-context-chunking.md) | Chunking strategy for long documents |

---

## Beta: Smart Memory v1.1.0

> Status: Beta — available via `npm i memory-lancedb-pro@beta`. Stable users on `latest` are not affected.

| Feature | Description |
|---------|-------------|
| **Smart Extraction** | LLM-powered 6-category extraction with L0/L1/L2 metadata. Falls back to regex when disabled. |
| **Lifecycle Scoring** | Weibull decay integrated into retrieval — high-frequency and high-importance memories rank higher. |
| **Tier Management** | Three-tier system (Core → Working → Peripheral) with automatic promotion/demotion. |

Feedback: [GitHub Issues](https://github.com/CortexReach/memory-lancedb-pro/issues) · Revert: `npm i memory-lancedb-pro@latest`

---

## Dependencies

| Package | Purpose |
| --- | --- |
| `@lancedb/lancedb` ≥0.26.2 | Vector database (ANN + FTS) |
| `openai` ≥6.21.0 | OpenAI-compatible Embedding API client |
| `@sinclair/typebox` 0.34.48 | JSON Schema type definitions |

---

## Contributors

<p>
<a href="https://github.com/win4r"><img src="https://avatars.githubusercontent.com/u/42172631?v=4" width="48" height="48" alt="@win4r" /></a>
<a href="https://github.com/kctony"><img src="https://avatars.githubusercontent.com/u/1731141?v=4" width="48" height="48" alt="@kctony" /></a>
<a href="https://github.com/Akatsuki-Ryu"><img src="https://avatars.githubusercontent.com/u/8062209?v=4" width="48" height="48" alt="@Akatsuki-Ryu" /></a>
<a href="https://github.com/JasonSuz"><img src="https://avatars.githubusercontent.com/u/612256?v=4" width="48" height="48" alt="@JasonSuz" /></a>
<a href="https://github.com/Minidoracat"><img src="https://avatars.githubusercontent.com/u/11269639?v=4" width="48" height="48" alt="@Minidoracat" /></a>
<a href="https://github.com/furedericca-lab"><img src="https://avatars.githubusercontent.com/u/263020793?v=4" width="48" height="48" alt="@furedericca-lab" /></a>
<a href="https://github.com/joe2643"><img src="https://avatars.githubusercontent.com/u/19421931?v=4" width="48" height="48" alt="@joe2643" /></a>
<a href="https://github.com/AliceLJY"><img src="https://avatars.githubusercontent.com/u/136287420?v=4" width="48" height="48" alt="@AliceLJY" /></a>
<a href="https://github.com/chenjiyong"><img src="https://avatars.githubusercontent.com/u/8199522?v=4" width="48" height="48" alt="@chenjiyong" /></a>
</p>

Full list: [Contributors](https://github.com/CortexReach/memory-lancedb-pro/graphs/contributors)

## Star History

<a href="https://star-history.dera.page/#CortexReach/memory-lancedb-pro&Date">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://star-history.dera.page/svg?repos=CortexReach/memory-lancedb-pro&type=Date&theme=dark&transparent=true" />
    <source media="(prefers-color-scheme: light)" srcset="https://star-history.dera.page/svg?repos=CortexReach/memory-lancedb-pro&type=Date&transparent=true" />
    <img alt="Star History Chart" src="https://star-history.dera.page/svg?repos=CortexReach/memory-lancedb-pro&type=Date&transparent=true" />
  </picture>
</a>

## License

MIT

---

## My WeChat QR Code

<img src="https://github.com/win4r/AISuperDomain/assets/42172631/7568cf78-c8ba-4182-aa96-d524d903f2bc" width="214.8" height="291">

---

### Overview (`examples/new-session-distill/README.md`)

# New Session Distillation (Recommended)

This example shows a **non-blocking /new distillation pipeline**:

- Trigger: `command:new` (when you type `/new`)
- Hook: enqueue a small JSON task file (fast, no LLM calls)
- Worker: a user-level systemd service watches the inbox and runs **Gemini Map-Reduce** over the session JSONL transcript
- Storage: write high-signal, atomic lessons into LanceDB Pro via `openclaw memory-pro import`
- Notify: send a notification message (optional)

Files included:
- `hook/enqueue-lesson-extract/` — OpenClaw workspace hook
- `worker/lesson-extract-worker.mjs` — Map-Reduce extractor + importer + notifier
- `worker/systemd/lesson-extract-worker.service` — user systemd unit

You must provide:
- `GEMINI_API_KEY` in an env file loaded by systemd

Install steps are documented in the main repo README.

---

### CHANGELOG V1.1.0 (`docs/CHANGELOG-v1.1.0.md`)

# memory-lancedb-pro v1.1.0 — 智能记忆增强

> **日期**: 2026-03-06  
> **作者**: CJY  
> **概述**: 基于对 AI Agent 记忆系统的深入理解，对记忆的写入质量、生命周期管理和去重能力进行了全面改进与完善

---

## 一、改进动机

原有记忆系统在**检索侧**表现优异（Vector+BM25 混合检索、cross-encoder 重排序、多维评分），但在以下方面存在提升空间：

- **记忆写入质量**：依赖正则表达式触发捕获，容易漏捕有价值信息或误捕噪声
- **记忆结构层次**：扁平文本存储，缺乏分层索引能力
- **记忆生命周期**：简单时间衰减，无法模拟人类记忆的遗忘与强化规律
- **去重能力**：仅基于向量相似度的粗粒度去重，缺乏语义级判断

本次改进针对这三个维度进行了系统性增强。

---

## 二、变更摘要

| 改进维度     | 核心变更                                  | 效果                               |
| ------------ | ----------------------------------------- | ---------------------------------- |
| 智能提取     | LLM 驱动的 6 类别提取 + L0/L1/L2 分层存储 | 记忆写入更精准、结构更丰富         |
| 生命周期管理 | Weibull 衰减模型 + 三层晋升/降级          | 重要记忆持久保留，过时记忆自然淡化 |
| 智能去重     | 向量预过滤 + LLM 语义决策                 | 避免冗余记忆，支持信息演化合并     |

### 当前落地状态（2026-03-06）

以下能力已接入运行主路径，而不再只是"模块存在"：

- `SmartExtractor` 已挂到 `agent_end`，新写入默认产出 `memory_category`、`tier`、`l0_abstract`、`l1_overview`、`l2_content`
- `before_agent_start` 自动召回后会回写 `access_count` 与 `last_accessed_at`
- `src/retriever.ts` 已接入 `decayEngine.applySearchBoost()`，检索结果按生命周期分数重排
- `tierManager` 已接入 recall 后维护流程，根据 recall 计数和 lifecycle score 回写 tier 变更
- `memory_store`、regex fallback、迁移、session memory、upgrade 都统一写入 smart metadata
- `openclaw.plugin.json` 已暴露 `decay` / `tier` 配置项
- 生命周期回归测试已加入 `npm test`

仍保留的兼容路径：

- 当 lifecycle decay 未启用或不可用时，retriever 仍可回退到旧的 `Recency Boost → Importance Weight → Time Decay` 排序链
- 旧格式数据仍可读取，并可通过 `openclaw memory-pro upgrade` 统一补齐 metadata

---

## 三、新增文件

### 1. `src/memory-categories.ts` — 6 类别分类系统

设计了语义明确的记忆分类体系，将记忆分为两大类六小类：

- **用户记忆**：`profile`（身份属性）、`preferences`（偏好习惯）、`entities`（持续存在的实体）、`events`（发生的事件）
- **Agent 记忆**：`cases`（问题-解决方案对）、`patterns`（可复用的处理流程）

每个类别有不同的合并策略：

- `profile` → 始终合并（用户身份信息持续累积）
- `preferences` / `entities` / `patterns` → 支持智能合并
- `events` / `cases` → 仅新增或跳过（独立记录，保留历史完整性）

---

### 2. `src/llm-client.ts` — LLM 客户端

封装了 LLM 调用接口，专注于结构化 JSON 输出：

- 复用现有 OpenAI SDK 依赖，零新增包
- 内置 JSON 容错解析：支持 markdown 代码块包裹和平衡大括号提取
- 低温度 (0.1) 保证输出一致性
- 30 秒超时保护，失败时优雅降级

---

### 3. `src/extraction-prompts.ts` — 记忆提取提示模板

精心设计了 3 个提示模板：

| 函数                      | 用途                                                |
| ------------------------- | --------------------------------------------------- |
| `buildExtractionPrompt()` | 从对话中提取 6 类别 L0/L1/L2 记忆，含 few-shot 示例 |
| `buildDedupPrompt()`      | CREATE / MERGE / SKIP 去重决策                      |
| `buildMergePrompt()`      | 将新旧记忆合并为三层结构                            |

提取提示包含完整的记忆价值判断标准、类别决策逻辑表、常见混淆澄清规则和 6 个 few-shot 示例。

---

### 4. `src/smart-extractor.ts` — 智能提取管线

实现了完整的 LLM 驱动提取流水线：

```
对话文本 → LLM 提取 → 候选记忆 → 向量去重 → LLM 决策 → 持久化
```

核心设计：

- **两阶段去重**：先用向量相似度（阈值 0.7）快速筛选候选，再用 LLM 进行语义级判断
- **类别感知合并**：不同类别应用不同合并策略
- **L0/L1/L2 三层存储**：L0 一句话索引用于检索注入，L1 结构化摘要用于精读，L2 完整叙述用于深度回顾
- **向后兼容**：新增的 6 类别自动映射到已有的 5 类别存储，L0/L1/L2 存储在 metadata JSON 中
- **按类别设定重要度**：profile (0.9) > patterns (0.85) > cases/preferences (0.8) > entities (0.7) > events (0.6)

---

### 5. `src/decay-engine.ts` — Weibull 衰减引擎

基于认知心理学中的记忆遗忘曲线研究，实现了复合衰减模型：

**复合分数 = 时效权重 × 时效 + 频率权重 × 频率 + 内在权重 × 内在价值**

三个分量：

| 分量                     | 机制                              | 含义                   |
| ------------------------ | --------------------------------- | ---------------------- |
| **时效 (recency)**       | Weibull 拉伸指数衰减 `exp(-λt^β)` | 越久远的记忆衰减越快   |
| **频率 (frequency)**     | 对数饱和曲线 + 时间加权           | 越常被访问的记忆越活跃 |
| **内在价值 (intrinsic)** | `importance × confidence`         | 高价值记忆天然抵抗遗忘 |

层级特定的衰减形状 (β 参数)：

- **Core** (β=0.8)：亚指数衰减 → 遗忘极慢，衰减地板 0.9
- **Working** (β=1.0)：标准指数衰减，衰减地板 0.7
- **Peripheral** (β=1.3)：超指数衰减 → 遗忘加速，衰减地板 0.5

关键特性：

- **重要性调制半衰期**：`effectiveHL = halfLife × exp(μ × importance)`，重要记忆持续更久
- **搜索结果加权**：检索时自动应用衰减加权，让活跃记忆排名更高
- **过期识别**：识别 composite < 0.3 的过期记忆

---

### 6. `src/tier-manager.ts` — 三层晋升/降级管理器

模拟人类记忆的多级存储模型：

```
Peripheral（外围） ⟷ Working（工作） ⟷ Core（核心）
```

**晋升条件**：

| 方向                 | 条件                                            |
| -------------------- | ----------------------------------------------- |
| Peripheral → Working | 访问次数 ≥ 3 且 衰减分数 ≥ 0.4                  |
| Working → Core       | 访问次数 ≥ 10 且 衰减分数 ≥ 0.7 且 重要度 ≥ 0.8 |

**降级条件**：

| 方向                 | 条件                                             |
| -------------------- | ------------------------------------------------ |
| Working → Peripheral | 衰减分数 < 0.15 或（年龄 > 60 天且访问次数 < 3） |
| Core → Working       | 衰减分数 < 0.15 且 访问次数 < 3（极少触发）      |

---

### 7. `src/smart-metadata.ts` — Smart metadata 归一化辅助模块

统一新老写入入口的生命周期字段，确保所有记忆路径产出一致的 metadata 结构：

- 统一补齐 `memory_category`、`tier`、`l0_abstract`、`l1_overview`、`l2_content`
- 统一处理 `access_count`、`confidence`、`last_accessed_at`
- 将存储记录转换为 lifecycle scoring 所需的 `DecayableMemory`
- 为迁移、upgrade、tool-store、regex fallback 提供同一套默认值与兼容逻辑

---

### 8. `src/memory-upgrader.ts` — 旧记忆升级器

将旧格式记忆（无 L0/L1/L2 / 5 类别）批量升级为新智能记忆格式，统一生命周期管理：

- **反向类别映射**：`fact→profile/cases`（含启发式+LLM 判断）、`preference→preferences`、`entity→entities`、`decision→events`、`other→patterns`
- **L0/L1/L2 生成**：LLM 模式（推荐）或 无 LLM 模式（首句截取+原文保留）
- **元数据补全**：自动填充 `tier: "working"`、`access_count: 0`、`confidence: 0.7`
- **批量处理**：支持 `--batch-size`、`--limit`、`--dry-run` 控制
- **启动检测**：插件启动 5 秒后自动检测旧记忆数量，日志提示升级

---

## 四、修改文件

### `index.ts` — 插件入口

#### 新增导入

```typescript
import { SmartExtractor } from "./src/smart-extractor.js";
import { createLlmClient } from "./src/llm-client.js";
import { createDecayEngine, DEFAULT_DECAY_CONFIG } from "./src/decay-engine.js";
import { createTierManager, DEFAULT_TIER_CONFIG } from "./src/tier-manager.js";
```

#### 新增配置项

```typescript
smartExtraction?: boolean;    // 是否启用 LLM 智能提取（默认 true）
llm?: {
  apiKey?: string;            // LLM API Key（默认复用 embedding.apiKey）
  model?: string;             // LLM 模型（默认 gpt-4o-mini）
  baseURL?: string;           // LLM API 端点
};
extractMinMessages?: number;  // 最少消息数才触发提取（默认 2）
extractMaxChars?: number;     // 送入 LLM 的最大字符数（默认 8000）
```

#### `agent_end` 钩子改进

- 当 `smartExtraction` 启用时，优先使用 SmartExtractor 进行 LLM 6 类别提取
- 当消息数不足或 SmartExtractor 未初始化时，降级回原有正则触发逻辑
- 提取完成后输出统计日志：`smart-extracted N created, M merged, K skipped`

#### `before_agent_start` 钩子改进

- 注入的记忆上下文现在显示 L0 摘要而非原始文本
- 新增 6 类别标签（如 `[preferences:global]`）
- 新增层级标记（`[C]`ore / `[W]`orking / `[P]`eripheral）
- 自动召回后回写 `access_count` 与 `last_accessed_at`

#### 启动时旧记忆检测

- 插件启动 5 秒后异步扫描旧格式记忆数量
- 如发现旧格式记忆，在日志中输出提示：`Run 'openclaw memory-pro upgrade' to convert them.`

### `src/retriever.ts` — 检索器

- 接入 `decayEngine.applySearchBoost()`，检索结果按生命周期分数重排
- 当 lifecycle decay 未启用或不可用时，可回退到旧的 `Recency Boost → Importance Weight → Time Decay` 排序链

### `openclaw.plugin.json` — 插件配置清单

- 暴露 `decay` / `tier` 配置项，允许用户自定义衰减参数和晋升阈值

### `cli.ts` — CLI 命令

#### 新增 `memory-pro upgrade` 命令

```bash
openclaw memory-pro upgrade [--dry-run] [--batch-size N] [--no-llm] [--limit N] [--scope SCOPE]
```

- `--dry-run`：仅统计旧记忆数量，不修改数据
- `--batch-size`：每批处理数量（默认 10）
- `--no-llm`：不调用 LLM，使用简单规则生成 L0/L1/L2
- `--limit`：最大升级数量
- `--scope`：仅升级指定 scope 的记忆

---

## 五、配置指南

### 最简配置（复用已有 API Key）

```json
{
  "embedding": {
    "apiKey": "${OPENAI_API_KEY}",
    "model": "text-embedding-3-small"
  },
  "smartExtraction": true
}
```

### 完整配置

```json
{
  "embedding": {
    "apiKey": "${OPENAI_API_KEY}",
    "model": "text-embedding-3-small"
  },
  "smartExtraction": true,
  "llm": {
    "apiKey": "${OPENAI_API_KEY}",
    "model": "gpt-4o-mini",
    "baseURL": "https://api.openai.com/v1"
  },
  "extractMinMessages": 2,
  "extractMaxChars": 8000
}
```

### 禁用智能提取

```json
{
  "smartExtraction": false
}
```

---

## 六、向后兼容性

| 方面           | 兼容方式                                       |
| -------------- | ---------------------------------------------- |
| LanceDB Schema | 新字段存储在 `metadata` JSON 中，不修改表结构  |
| 记忆类别       | 新 6 类别自动映射到原有 5 类别                 |
| 混合检索       | Vector+BM25 检索管线完全保留                   |
| 去重逻辑       | 仅在 `smartExtraction: true` 时生效            |
| 已有数据       | 旧记忆正常读取，新记忆额外携带 L0/L1/L2 元数据 |
| 配置           | 全部新增配置项均有默认值，零配置即可使用       |
| **旧记忆升级** | `memory-pro upgrade` 命令一键升级为新格式      |

---

### Lock Management (`docs/lock-management.md`)

# Lock Management

`memory-lancedb-pro` protects LanceDB writes with a cross-process file lock based on
`proper-lockfile`. The lock target is `.memory-write.lock` inside the configured
database directory, and the transient lock artifact is `.memory-write.lock.lock`.

## When The Built-In File Lock Is Enough

Use the default file lock for:

- a single OpenClaw gateway process
- multiple agents writing through the same gateway process
- multiple local processes that share the same local filesystem path

The store also batches concurrent `bulkStore()` calls before taking the write
lock, so high-throughput auto-capture paths avoid one lock acquisition per
memory item.

## Multi-Machine Or Container Deployments

The file lock only coordinates processes that can see the same lock target on a
filesystem with reliable lock-directory semantics. For multi-machine,
multi-container, or network-filesystem deployments, verify this before relying on
the default lock:

```bash
openclaw memory-pro stats --json
ls -la "$(dirname "$MEMORY_LANCEDB_PRO_DB_PATH")"
```

If each process has its own local database directory, no lock can protect writes
across those processes because they are not writing to the same LanceDB store.

## Redis Status

Redis is not required for single-gateway or same-filesystem deployments. The
plugin enables Redis locking when `locking.redis.enabled` is true, when
`redisUrl`/`locking.redis.url` is set, or when `MEMORY_LANCEDB_REDIS_URL` is
present in that process environment. Every writer that shares a LanceDB store
must use the same Redis lock configuration; mixing Redis-locked writers with
writers that only use the local file lock creates separate lock domains.

When Redis locking is enabled, every LanceDB write and index-maintenance
mutation uses the Redis lock domain. Redis connection or lock-client
availability failures fail writes closed instead of falling back to a local file
lock.

If you operate multiple independent writers, prefer one of these approaches:

- route writes through one OpenClaw gateway
- place the LanceDB directory on a filesystem whose lock-directory behavior is
  known to work with `proper-lockfile`
- add an explicit external write coordinator before enabling multiple writers

Do not use a no-op lock fallback for write paths. If an external coordinator is
required but unavailable, fail the write and retry after the coordinator is
healthy rather than switching that writer into a separate local lock domain.

## Troubleshooting Lock Contention

Symptoms of lock contention include slow memory writes, delayed auto-capture, or
warnings about stale `.memory-write.lock.lock` artifacts.

Checklist:

1. Confirm all writers use the same `dbPath`.
2. Confirm old `.memory-write.lock.lock` artifacts are cleaned up by the store.
3. Prefer `bulkStore()` for batch writes instead of repeated `store()` calls.
4. Avoid multiple independent gateway processes writing to separate copies of the
   same intended memory corpus.

---

### Long Context Chunking (`docs/long-context-chunking.md`)

# Long Context Chunking

## Overview

The long context chunking system automatically handles documents that exceed embedding model context limits by splitting them into manageable chunks and computing averaged embeddings.

## Problem Solved

When embedding very long documents or messages, you might encounter errors like:

```
Input length exceeds context length: 12453 tokens. Maximum length: 8192 tokens.
```

This plugin now handles such cases gracefully by:
1. Detecting context length errors before they cause failures
2. Automatically splitting the document into overlapping chunks
3. Embedding each chunk separately
4. Computing an averaged embedding that preserves semantic meaning

## How It Works

### Chunking Strategy

The chunker uses a **semantic-aware** approach:

- **Splits at sentence boundaries** when possible (better for preserving meaning)
- **Configurable overlap** (default: 200 characters) to maintain context across chunks
- **Adapts to model context limits** based on the embedding model
- **Forced splits** at hard limits if sentence boundaries are not found

### Chunking Flow

```
Long Document
    │
    ├── 8192+ characters ──┐
                            │
                            ▼
                ┌─────────────────┐
                │  Detect Overflow │
                └────────┬────────┘
                         │
                         ▼
                ┌─────────────────┐
                │  Split into     │
                │  Overlapping     │
                │  Chunks          │
                └────────┬────────┘
                         │
    ┌────────────────────┼────────────────────┐
    │                    │                    │
    ▼                    ▼                    ▼
┌────────┐         ┌────────┐         ┌────────┐
│ Chunk 1│         │ Chunk 2│         │ Chunk 3│
│  [1-2k]│         │[1.8k-3.8k]│    │[3.6k-5.6k]│
└───┬────┘         └───┬────┘         └───┬────┘
    │                  │                  │
    ▼                  ▼                  ▼
Embedding          Embedding          Embedding
    │                  │                  │
    └──────────────────┼──────────────────┘
                       │
                       ▼
              Compute Average
                       │
                       ▼
              Final Embedding
```

## Configuration

### Default Settings

The chunker automatically adapts to your embedding model:

- **maxChunkSize**: 70% of model context limit (e.g., 5734 for 8192-token model)
- **overlapSize**: 5% of model context limit
- **minChunkSize**: 10% of model context limit
- **semanticSplit**: true (prefer sentence boundaries)
- **maxLinesPerChunk**: 50 lines

### Disabling Auto-Chunking

If you prefer to handle chunking manually or want the model to fail on long documents:

```json
{
  "plugins": {
    "entries": {
      "memory-lancedb-pro": {
        "enabled": true,
        "config": {
          "embedding": {
            "apiKey": "${JINA_API_KEY}",
            "model": "jina-embeddings-v5-text-small",
            "chunking": false  // Disable auto-chunking
          }
        }
      }
    }
  }
}
```

### Custom Chunking Parameters

For advanced users who want to tune chunking behavior:

```json
{
  "plugins": {
    "entries": {
      "memory-lancedb-pro": {
        "enabled": true,
        "config": {
          "embedding": {
            "autoChunk": {
              "maxChunkSize": 2000,      // Characters per chunk
              "overlapSize": 500,          // Overlap between chunks
              "minChunkSize": 500,         // Minimum acceptable chunk size
              "semanticSplit": true,       // Prefer sentence boundaries
              "maxLinesPerChunk": 100      // Max lines before forced split
            }
          }
        }
      }
    }
  }
}
```

## Supported Models

The chunker automatically adapts to these embedding models:

| Model | Context Limit | Chunk Size | Overlap |
|-------|---------------|------------|----------|
| Jina jina-embeddings-v5-text-small | 8192 | 5734 | 409 |
| OpenAI text-embedding-3-small | 8192 | 5734 | 409 |
| OpenAI text-embedding-3-large | 8192 | 5734 | 409 |
| Gemini gemini-embedding-001 | 2048 | 1433 | 102 |

## Performance Considerations

### Token Savings

- **Without chunking**: 1 failed embedding (retries required)
- **With chunking**: 3-4 chunk embeddings (1 avg result)
- **Net cost increase**: ~3x for long documents (>8k tokens)
- **Trade-off**: Gracefully handling vs. processing smaller documents

### Caching

Chunked embeddings are cached by their original document hash, so:
- Subsequent requests for the same document get the cached averaged embedding
- Cache hit rate improves as long documents are processed repeatedly

### Processing Time

- **Small documents (<4k chars)**: No chunking, same as before
- **Medium documents (4k-8k chars)**: No chunking, same as before
- **Long documents (>8k chars)**: ~100-200ms additional chunking overhead

## Logging & Debugging

### Enable Debug Logging

To see chunking in action, you can check the logs:

```
Document exceeded context limit (...), attempting chunking...
Split document into 3 chunks for embedding
Successfully embedded long document as 3 averaged chunks
```

### Common Scenarios

**Scenario 1: Long memory text**
- When a user's message or system prompt is very long
- Automatically chunked before embedding
- No error thrown, memory is still stored and retrievable

**Scenario 2: Batch embedding long documents**
- If some documents in a batch exceed limits
- Only the long ones are chunked
- Successful documents processed normally

## Troubleshooting

### Chunking Still Fails

If you still see context length errors:

1. **Verify model**: Check which embedding model you're using
2. **Increase minChunkSize**: May need smaller chunks for some models
3. **Disable autoChunk**: Handle chunking manually with explicit split

### Too Many Small Chunks

If chunking creates many tiny fragments:

1. **Increase minChunkSize**: Larger minimum chunk size
2. **Reduce overlap**: Less overlap between chunks means more efficient chunks

### Embedding Quality Degradation

If chunked embeddings seem less accurate:

1. **Increase overlap**: More context between chunks preserves relationships
2. **Use smaller maxChunkSize**: Split into more, smaller overlapping pieces
3. **Consider hierarchical approach**: Use a two-pass retrieval (chunk → document → full text)

## Future Enhancements

Planned improvements:

- [ ] **Hierarchical chunking**: Chunk → document-level embedding
- [ ] **Sliding window**: Different overlap strategies per document complexity
- [ ] **Smart summarization**: Summarize chunks before averaging for better quality
- [ ] **Context-aware overlap**: Dynamic overlap based on document complexity
- [ ] **Async chunking**: Process chunks in parallel for batch operations

## Technical Details

### Algorithm

1. **Detect overflow**: Check if document exceeds maxChunkSize
2. **Split semantically**: Find sentence boundaries within target range
3. **Create overlap**: Include overlap with previous chunk's end
4. **Embed in parallel**: Process all chunks simultaneously
5. **Average the result**: Compute mean embedding across all chunks

### Complexity

- **Time**: O(n × k) where n = number of chunks, k = average chunk processing time
- **Space**: O(n × d) where d = embedding dimension

### Edge Cases

| Case | Handling |
|------|----------|
| Empty document | Returns empty embedding immediately |
| Very small documents | No chunking, normal processing |
| Perfect boundaries | Split at sentence ends, no truncation |
| No boundaries found | Hard split at max position |
| Single oversized chunk | Process as-is, let provider error |
| All chunks too small | Last chunk takes remaining text |

## References

- [LanceDB Documentation](https://lancedb.com)
- [OpenAI Embedding Context Limits](https://platform.openai.com/docs/guides/embeddings)
- [Semantic Chunking Research](https://arxiv.org/abs/2310.05970)

---

*This feature was added to handle long-context documents gracefully without losing memory quality.*

---

### Memory Architecture Analysis (`docs/memory_architecture_analysis.md`)

# `memory-lancedb-pro` 记忆架构分析

> 更新时间：2026-03-09  
> 基准：当前仓库中的 `README.md`、`openclaw.plugin.json`、`index.ts`、`src/*`、`cli.ts`  
> 结论：当前版本已经形成完整的“写入 -> 存储 -> 检索 -> 生命周期维护 -> 运维工具”闭环；智能提取、嵌入噪声检测、生命周期衰减、Tier 晋升/降级、迁移升级、CLI 与 Agent Tools 都已纳入同一主架构。

---

## 一、架构判断摘要

### 1.1 当前真实主路径

当前版本不是“在旧向量记忆插件上补了几个实验模块”，而是一个完整的 OpenClaw Memory Plugin：

- `index.ts` 负责插件注册、配置解析、Hook 装配、CLI 装配、后台服务装配
- `src/store.ts` 负责 LanceDB 表初始化、CRUD、向量检索、FTS/BM25、metadata 局部回写
- `src/embedder.ts` 负责统一 Embedding Provider 适配、任务感知 embedding、长文本 chunking、缓存
- `src/retriever.ts` 负责混合检索、rerank、长度归一化、生命周期重排、噪声过滤、去重多样性
- `src/smart-extractor.ts` 负责 LLM 智能提取、嵌入噪声预过滤、两阶段去重、语义合并、新格式写入
- `src/noise-prototypes.ts` 负责嵌入噪声原型库（语言无关噪声检测 + LLM 反馈学习闭环）
- `src/decay-engine.ts` + `src/tier-manager.ts` 负责 recall 后的生命周期评分与 tier 演化
- `src/tools.ts` 和 `cli.ts` 分别提供 Agent Tool 面与运维 CLI 面

### 1.2 当前版本最重要的架构特征

- 智能提取已经进入默认写入主路径，regex 仅作为兜底
- 生命周期衰减已经进入默认检索主路径，不再是“代码里有对象但没接上”
- smart metadata 已成为全系统的统一中间层
- 旧 5 类 store category 与新 6 类 memory category 处于“双层兼容模型”
- auto-recall、tool recall、CLI、迁移、升级、session memory 都共享同一份 LanceDB 数据平面

### 1.3 仍保留的兼容层

- 无 FTS 时自动回退到 vector-only / lexical fallback
- 无 lifecycle decay 时回退到旧的 `recency boost + importance weight + time decay`
- legacy memory 仍能读；可通过 `memory-pro upgrade` 补齐 smart metadata
- 存储表 schema 未拆分成多张新表，而是保留单表 + `metadata` JSON 扩展

---

## 二、总体架构图

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

## 三、入口层：`index.ts` 是总装配器

`index.ts` 不是简单导出插件对象，而是整个系统的总装配器，负责把各模块接入 OpenClaw：

- 解析配置并做 env var 展开
- 创建 `MemoryStore`、`Embedder`、`MemoryRetriever`、`ScopeManager`
- 可选初始化 `SmartExtractor`
- 注册 Agent Tools
- 注册 `memory-pro` CLI
- 注册 `before_agent_start`、`agent_end`、`command:new`
- 注册后台 service：启动检查、legacy upgrade 提示、自动备份

### 3.1 运行时默认值

按当前实现，关键默认行为如下：

- `smartExtraction`: 默认开启
- `extractMinMessages`: 默认 `2`
- `autoCapture`: 默认开启
- `autoRecall`: 默认关闭
- `captureAssistant`: 默认关闭
- `enableManagementTools`: 默认关闭
- `sessionMemory`: 只有显式 `sessionMemory.enabled === true` 才注册 Hook，运行时默认关闭

### 3.2 服务层职责

插件 `registerService().start()` 不是业务核心链路，但它负责运维闭环：

- 异步执行 `embedder.test()` 与 `retriever.test()`，避免阻塞 Gateway 启动
- 启动后约 5 秒检查 legacy memory 数量并输出 upgrade 提示
- 启动后 1 分钟做一次 JSONL 备份，此后每 24 小时做一次
- 停止时清理定时器

这意味着该插件不仅有“读写能力”，也有“自检 + 自提示 + 备份”的运维能力。

---

## 四、写入架构：从对话到长期记忆

### 4.1 写入入口清单

当前写入来源不止一条：

- `agent_end` 自动捕获
- `memory_store` Agent Tool 手工写入
- `memory_update` Agent Tool 原地更新
- `command:new` 的 session summary 写入
- `memory-pro import`
- `memory-pro migrate run`
- `memory-pro upgrade`
- `memory-pro reembed`

其中自动捕获是最核心的在线写入链路。

### 4.2 自动捕获写入主路径

当前自动捕获的真实入口已经不是“裸 `agent_end.messages` 直读”，而是一个轻量的运行时适配层：

```text
message_received
  -> 归一化实时入站文本（去 addressing / 去 OpenClaw 注入前缀）
  -> 按 conversationKey 暂存最近少量原始用户文本

agent_end
  -> 从 event.messages 提取 user/assistant 文本
  -> 再做同一套归一化（去 recall block / inbound metadata / session reset prompt / addressing）
  -> 优先消费 pending ingress texts
  -> 若无 pending ingress，则退回到 session snapshot 的“新增文本增量”
  -> 若只有显式 remember 指令，则可补上一条最近真实文本形成最小上下文
  -> 进入 smart extraction / regex fallback 主链
```

进入写入主链后的执行顺序是：

```text
抽取并归一化文本（默认仅 `user`；当 `captureAssistant === true` 时也包含 `assistant`）
  -> 若 smartExtractor 可用:
       嵌入噪声预过滤 filterNoiseByEmbedding(texts)
       若所有文本被过滤 -> return（纯噪声输入）
       若清洗后消息数 >= extractMinMessages:
            SmartExtractor.extractAndPersist()
            若 created > 0 或 merged > 0，则直接 return
            否则继续走 regex fallback
  -> shouldCapture() + detectCategory() + regex fallback
  -> buildSmartMetadata()
  -> store.store()
```

几个关键点：

- 当前实现增加了一层“OpenClaw runtime 适配层”，用来消解 `prependContext`、inbound metadata、addressing、session snapshot 粒度等上游差异
- 智能提取只有在产出持久化结果时，才会接管整个自动捕获路径
- regex fallback 仍存在，但已经不再输出“半旧格式数据”
- fallback 写入前仍做噪声过滤与重复检查
- 每轮对话最多写入 3 条 fallback memory
- 当前插件不会主动回读 session JSONL 或 LanceDB 历史来拼装捕获输入；运行时上下文只来自：
  - `message_received` 的短暂内存队列
  - `agent_end.event.messages`
  - 当前进程内的极小 recent-text 缓存
- 这层适配是 OpenClaw runtime 通用策略，不绑定 Discord/Telegram 等具体渠道协议字段

### 4.3 SmartExtractor 主链

`src/smart-extractor.ts` 的核心流程是：

```text
texts
  -> filterNoiseByEmbedding() 嵌入噪声预过滤
  -> conversationText
  -> LLM 抽取 CandidateMemory[]
  -> 逐条向量预筛
  -> LLM 判定 CREATE / MERGE / SKIP
  -> buildSmartMetadata()
  -> store.store() / store.update()
  -> 若 zero candidates -> learnAsNoise() 反馈到噪声库
```

当前特征：

- 候选记忆最多处理 5 条
- 第一阶段去重使用 `vectorSearch(..., threshold=0.7)`
- 第二阶段去重交给 LLM 做语义决策
- `profile` 类记忆永远优先走 merge
- `events` / `cases` 更偏 append-only

### 4.4 6 类智能分类与 5 类存储分类并存

这是当前架构最重要的兼容设计之一。

### 智能分类

定义在 `src/memory-categories.ts`：

- `profile`
- `preferences`
- `entities`
- `events`
- `cases`
- `patterns`

### 存储分类

底层 LanceDB 主字段 `category` 仍保留旧 5 类：

- `preference`
- `fact`
- `decision`
- `entity`
- `other`

### 映射关系

`SmartExtractor` 写入时会做兼容映射：

| 智能分类 | 存储分类 |
| --- | --- |
| `profile` | `fact` |
| `preferences` | `preference` |
| `entities` | `entity` |
| `events` | `decision` |
| `cases` | `fact` |
| `patterns` | `other` |

因此：

- 结构化语义分类保存在 `metadata.memory_category`
- 向后兼容的粗粒度分类保存在 `entry.category`

也正因为如此，CLI / Tool 里的 `category` 过滤目前仍以旧 5 类为准。

### 4.5 `memory_update` 的定位

`memory_update` 是新版里值得单独强调的能力：

- 支持按完整 UUID 或 8+ 前缀定位 memory
- 文本更新时自动重新 embedding
- 保留原始 `timestamp`
- 适合修正陈旧事实，而不是“删旧建新”

这意味着系统现在支持“记忆更正”而不只是“记忆追加”。

### 4.6 嵌入噪声检测：NoisePrototypeBank

`src/noise-prototypes.ts` 提供了语言无关的噪声检测能力，替代基于正则的方式：

- 内置 ~15 条多语言噪声原型（回忆查询、agent 否认、问候语）
- 插件启动时嵌入所有原型并缓存向量（异步非阻塞）
- auto-capture 管道中，SmartExtractor 的 `filterNoiseByEmbedding()` 在 LLM 提取前执行
- 文本长度分层策略：<=8 字符跳过（防误判）、8-300 字符做嵌入检查、>300 字符跳过（长文本非噪声）
- 默认相似度阈值 `0.82`

**LLM 反馈闭环：**

当 LLM 提取返回零候选时，`learnAsNoise()` 将该文本的嵌入向量加入噪声库：

```text
文本 -> 嵌入检查 -> [未命中] -> LLM 提取 -> 返回 {memories: []} 
                                          |
                              将该文本向量加入噪声库
                                          |
                          下次类似文本直接被嵌入检查拦截
```

- 去重：新向量与已有原型相似度 >= 0.95 则跳过
- 容量上限：最多学习 200 条，超限淘汰最旧的学习原型（保留内置原型）
- 仅在 `smartExtraction !== false` 时激活，不影响纯 regex 路径

---

## 五、统一元数据层：`smart-metadata.ts` 是系统中枢

`src/smart-metadata.ts` 是整套架构的真正收敛点。

它负责：

- 解析 legacy metadata
- 用 entry 本身补齐缺省字段
- 将新旧 memory 归一化成统一 metadata 结构
- 将 entry 转换为 lifecycle scoring 所需的 `LifecycleMemory`

### 5.1 当前 metadata 常见字段

| 字段 | 含义 |
| --- | --- |
| `memory_category` | 新 6 类语义分类 |
| `tier` | `core` / `working` / `peripheral` |
| `l0_abstract` | 短句索引，默认也是主搜索文本 |
| `l1_overview` | 结构化摘要 |
| `l2_content` | 长叙述内容 |
| `access_count` | 被 recall 的次数 |
| `confidence` | 抽取/合并置信度 |
| `last_accessed_at` | 最近被 recall 时间 |
| `source_session` | 来源 session key |
| `type` | 特殊类型，如 `session-summary` |
| `tier_updated_at` | tier 最近更新时间 |
| `migratedFrom` / `originalId` | 迁移/升级来源信息 |

### 5.2 为什么 metadata 是核心设计

当前实现没有把 L0/L1/L2、tier、access_count 单独提升为 LanceDB 表字段，而是统一放进 `metadata` JSON。这样做带来四个结果：

- 不需要重建 LanceDB 主表
- legacy entry 能平滑升级
- store 层的 CRUD 复杂度保持可控
- lifecycle、smart extraction、迁移、session memory 可以共享同一格式

---

## 六、存储层：`MemoryStore` 不只是 CRUD

`src/store.ts` 的职责比 README 表里写的更重。

### 6.1 初始化阶段

`MemoryStore` 是 lazy-init 的：

- 首次访问时连接 LanceDB
- 若表不存在则创建 `memories`
- 创建或检测 FTS index
- 校验 vector dimension 是否匹配

因此 store 既是数据库访问层，也是 schema 守门层。

### 6.2 查询能力

它提供三种检索能力：

- `vectorSearch()`
- `bm25Search()`
- `lexicalFallbackSearch()`

其中 BM25 路径是：

```text
有 FTS index -> LanceDB search(query, "fts")
无 FTS / FTS 失败 -> lexicalFallbackSearch()
```

lexical fallback 不只匹配 `entry.text`，还会参考：

- `l0_abstract`
- `l1_overview`
- `l2_content`

这意味着即使 FTS 不可用，smart metadata 仍然参与词法命中。

### 6.3 更新能力

当前 LanceDB 不支持真正的 in-place update，所以 `store.update()` 采用：

```text
读旧行 -> 构造新 entry -> delete old -> add new
```

而 `patchMetadata()` 则是在这层之上封装的 metadata 局部更新入口，被以下链路复用：

- recall 访问计数更新
- tier 回写
- 未来的 lifecycle 扩展字段更新

### 6.4 运维相关能力

store 层还承接了多项运维需求：

- `delete()` 支持 UUID 与前缀删除
- `bulkDelete()` 强制要求 filter，避免误删全库
- `importEntry()` 保留源 `id/timestamp`，用于 reembed / migration
- `stats()` 为 CLI 和管理工具提供聚合统计
- `hasId()` 用于过滤 BM25 ghost entry

---

## 七、Embedding 层：Provider 适配 + Chunking + Cache

`src/embedder.ts` 在当前架构里不是简单的 OpenAI SDK 包装，而是完整的 embedding 抽象层。

### 7.1 主要能力

- OpenAI-compatible provider 统一接入
- `taskQuery` / `taskPassage` 任务感知 embedding
- `dimensions` 透传
- `normalized` 透传
- LRU + TTL embedding cache
- 长文本自动 chunking

### 7.2 长文本 chunking 的角色

`src/chunker.ts` 为 embedding 超长文本提供模型感知分块：

- 超过上下文限制时触发
- 按语义边界切块并带 overlap
- 分块 embedding 后做平均向量聚合

这条能力对以下场景尤其重要：

- session summary
- 导入的长文档 memory
- reembed/import 场景

从实现上看，embedder 当前默认开启 auto-chunking。

---

## 八、检索架构：混合检索已经是默认主线

`src/retriever.ts` 是当前系统的在线 recall 核心。

### 8.1 检索入口

当前有三类读取入口共用 retriever：

- `before_agent_start` 自动召回
- `memory_recall` Agent Tool
- `memory-pro search` CLI

### 8.2 混合检索主链

在有 FTS 支持时，默认路径是：

```text
embedQuery(query)
  -> vectorSearch
  -> bm25Search
  -> fusion
  -> rerank
  -> length normalization
  -> hard min score
  -> lifecycle decay boost / legacy time decay
  -> noise filter
  -> MMR diversity
```

更具体地说：

1. 向量检索和 BM25 并行执行  
2. `fuseResults()` 以向量分数为主，BM25 命中提供确认性加权  
3. 可选 Cross-Encoder rerank，失败则降级为 cosine rerank  
4. 长文本进行长度惩罚  
5. 过低语义相关度结果直接硬过滤  
6. 生命周期分数再做最终重排  
7. 去噪与近重复打散

### 8.3 当前融合策略不是教科书式 RRF

README 把它描述为 hybrid fusion，但代码中的实际策略更偏“向量分数主导 + BM25 奖励”：

- 命中向量时，以向量分数为主
- BM25 命中可提供额外 boost
- 纯 BM25 命中允许直接进入结果集
- 对高 BM25 精确词法命中设置 preservation floor，避免被 reranker 错杀

这是为了照顾：

- 配置项、ID、token、环境变量等符号型检索
- 中英混合 query
- reranker 对符号查询不稳定的现实问题

### 8.4 当前排序链上的关键实现细节

- `hardMinScore` 发生在 decay/time-decay 之前
- lifecycle decay 在有 `decayEngine` 时替代旧的 recency/importance/time-decay 路径
- `applyMMRDiversity()` 不是删除相似项，而是把相似项延后
- BM25-only 结果会先用 `store.hasId()` 检查，避免 FTS 残留 ghost entry

---

## 九、生命周期闭环：现在已经接入业务运行时

`src/decay-engine.ts` 与 `src/tier-manager.ts` 在当前版本里已经不再是离线概念模型，而是运行时逻辑。

### 9.1 Decay Engine

Decay 的复合分数公式是：

```text
composite = recencyWeight * recency
          + frequencyWeight * frequency
          + intrinsicWeight * intrinsic
```

其中：

- `recency`: Weibull 拉伸指数衰减，半衰期受 importance 调制
- `frequency`: 访问次数的对数饱和 + 最近访问模式奖励
- `intrinsic`: `importance * confidence`

Tier 还会影响衰减形状：

| Tier | beta | floor | 含义 |
| --- | --- | --- | --- |
| `core` | `0.8` | `0.9` | 衰减最慢，搜索最低保留高 |
| `working` | `1.0` | `0.7` | 中性 |
| `peripheral` | `1.3` | `0.5` | 衰减最快 |

### 9.2 Tier Manager

Tier 迁移规则大致如下：

```mermaid
graph LR
    P["Peripheral"] -->|"access >= 3 且 composite >= 0.4"| W["Working"]
    W -->|"access >= 10 且 composite >= 0.7 且 importance >= 0.8"| C["Core"]
    W -->|"composite < 0.15 或 高龄低访问"| P
    C -->|"极低 composite 且低访问"| W
```

### 9.3 生命周期触发点

当前需要特别区分两个 recall 路径：

### auto-recall 路径

`before_agent_start` 在拿到 recall 结果后会执行完整闭环：

1. `patchMetadata(access_count + 1, last_accessed_at = now)`
2. 读取同 scope 的近期记忆
3. `decayEngine.scoreAll()`
4. `tierManager.evaluateAll()`
5. 将 `tier` / `tier_updated_at` 回写

### `memory_recall` Tool 路径

`memory_recall` 当前会：

- 更新 `access_count`
- 更新 `last_accessed_at`

但不会调用 `runRecallLifecycle()`，也就是说：

- Tool recall 会积累访问数据
- 真正的 tier 迁移维护目前仍只挂在 auto-recall 主链上

这是当前架构里一个重要而容易被忽略的边界。

### 9.4 session-summary 的特殊处理

`sessionMemory` 写入的 memory 会带 `metadata.type = "session-summary"`。  
在 auto-recall 生命周期维护中，这类 memory 会被排除在 tier 评估之外，避免会话摘要挤占核心长期记忆层级。

---

## 十、读取注入层：auto-recall 的上下文安全模型

当 `autoRecall === true` 时，`before_agent_start` 会把 recall 结果注入到：

```xml
<relevant-memories>
...
</relevant-memories>
```

几个值得注意的设计点：

- 默认关闭，避免模型直接回显 memory block
- 注入前先走 `shouldSkipRetrieval()`，避免问候语、小确认、emoji、slash 命令触发检索
- 注入文本带 `[UNTRUSTED DATA]` 提示，防止把 memory 当指令执行
- 显示内容优先使用 `l0_abstract`
- 显示时会附上 category、scope、tier、分数与来源标记

因此 auto-recall 实际上已经包含：

- 召回判断
- 召回结果格式化
- prompt injection 安全隔离
- recall 后 lifecycle 驱动

---

## 十一、Scope 隔离：这是多 Agent 记忆的边界层

`src/scopes.ts` 负责 scope 体系与访问控制。

支持的 scope 形态：

- `global`
- `agent:<id>`
- `project:<id>`
- `user:<id>`
- `custom:<name>`

运行时规则：

- agent 默认可见 `global + agent:<id>`
- 可通过 `scopes.agentAccess` 细化访问范围
- Tool 与 Hook 都会在运行时解析 agentId，再做 scope 过滤

这意味着该插件已经是“多租户记忆系统”，而不是单用户单库的简单记忆表。

---

## 十二、工具面与 CLI 面：这不是附属功能，而是第二操作平面

### 12.1 Agent Tools

核心工具现在是 4 个：

- `memory_recall`
- `memory_store`
- `memory_forget`
- `memory_update`

可选管理工具：

- `memory_stats`
- `memory_list`

注意：

- 核心 4 个默认注册
- 管理工具只有 `enableManagementTools === true` 才注册

### 12.2 CLI

`memory-pro` 当前已经具备完整运维面：

- `version`
- `list`
- `search`
- `stats`
- `delete`
- `delete-bulk`
- `export`
- `import`
- `reembed`
- `upgrade`
- `migrate check`
- `migrate run`
- `migrate verify`

其中几条尤其关键：

- `upgrade`: 把 legacy memory 升级成 smart format
- `migrate`: 从旧 `memory-lancedb` 迁移
- `reembed`: 用新 embedding 模型重建向量但保留 ID/时间
- `delete-bulk`: 带 scope/before 保护条件的批量删除

从架构角度看，这些 CLI 不是外部脚本，而是插件运行面的一部分。

---

## 十三、兼容与演进：迁移、升级、备份已经内建

### 13.1 迁移

`src/migrate.ts` 负责从旧版 `memory-lancedb` 导入数据：

- 自动搜索常见 legacy DB 路径
- 读取旧 `memories` 表
- 迁移到新库
- 同时补 smart metadata，而不是简单复制旧字段

### 13.2 升级

`src/memory-upgrader.ts` 负责对“已经在 Pro 库里但仍是旧格式”的 memory 做升级：

- 统计 legacy memory 数量
- 支持 LLM 模式与 no-LLM 模式
- 为旧数据补全 L0/L1/L2、category、tier、confidence 等

### 13.3 自动备份

当前 service 会定时导出 JSONL 备份：

- 启动后 1 分钟首次执行
- 后续每 24 小时执行
- 默认保留最近 7 份

这份备份更像“内容与 metadata 快照”，不是完整向量副本，因为 `store.list()` 默认不带 vectors。

---

## 十四、会话记忆与批处理蒸馏：两条扩展型摄入链路

### 14.1 Session Memory

`command:new` Hook 在启用时会：

- 查找上一段 session JSONL
- 抽取最近 N 条消息
- 生成 session summary memory
- 写入 LanceDB，并打上 `type = session-summary`

但当前 README 与代码都明确表达了一个操作建议：

- 这条能力默认不启用
- 因为大段 session summary 容易污染高质量检索

### 14.2 `jsonl_distill.py`

README 新增的批处理方案是另一条非常值得记录的架构支线：

- 通过 `scripts/jsonl_distill.py` 增量读取 session JSONL
- 过滤噪声
- 用专门 distiller agent 提炼高信号 lesson
- 最终通过 `memory_store` 或 `memory-pro import` 回灌主库

这条链路说明本项目现在已经从“在线对话记忆插件”延伸到“离线记忆蒸馏平台”。

---

## 十五、测试覆盖说明

当前与新架构强相关的测试包括：

- `test/smart-memory-lifecycle.mjs`
- `test/retriever-rerank-regression.mjs`
- `test/smart-extractor-branches.mjs`
- `test/cli-smoke.mjs`

这些测试分别覆盖：

- legacy metadata 归一化
- decay-aware retrieval 排序正确性
- tier promotion / demotion
- rerank 对强词法命中的保护
- smart extractor 的 merge / skip 分支
- CLI 的基础闭环

说明这套新架构已经不只是 README 层叙事，而是被纳入回归验证。

---

## 十六、核心文件职责索引

| 文件 | 当前职责 |
| --- | --- |
| `index.ts` | 插件总装配器：配置解析、Hook、Tool、CLI、Service、生命周期闭环 |
| `src/store.ts` | LanceDB 存储层：初始化、FTS、vector/BM25、CRUD、metadata patch |
| `src/embedder.ts` | Embedding 抽象层：provider 适配、task embedding、chunking、cache |
| `src/chunker.ts` | 长文本分块器 |
| `src/retriever.ts` | 混合检索、rerank、length norm、decay boost、MMR |
| `src/smart-extractor.ts` | LLM 提取、嵌入噪声预过滤、两阶段去重、merge/create/skip、持久化 |
| `src/smart-metadata.ts` | metadata 归一化、兼容映射、lifecycle 视图转换 |
| `src/decay-engine.ts` | Weibull 衰减模型、搜索分数 boost |
| `src/tier-manager.ts` | 三层记忆晋升/降级 |
| `src/scopes.ts` | 多 scope 隔离与 agent 访问控制 |
| `src/tools.ts` | Agent Tools 平面 |
| `cli.ts` | 运维 CLI 平面 |
| `src/migrate.ts` | 从旧插件迁移 |
| `src/memory-upgrader.ts` | 同库旧格式升级 |
| `src/noise-filter.ts` | 写入/检索噪声过滤（regex 规则） |
| `src/noise-prototypes.ts` | 嵌入噪声原型库：语言无关噪声检测 + LLM 反馈学习 |
| `src/adaptive-retrieval.ts` | auto-recall 检索守卫 |

---

## 十七、一句话总结

`memory-lancedb-pro` 当前的真实架构可以概括为：

**用 `smart-metadata` 把新旧 memory 统一到一套数据平面，用 `SmartExtractor` 提升写入质量，用 `Retriever + Decay + Tier` 提升召回质量，再用 Tools / CLI / Migration / Backup 把它补成一个可运营的长期记忆子系统。**

---

### Openclaw Dream Retrofit Report (`docs/openclaw-dream-retrofit-report.md`)

# OpenClaw Dream Retrofit Final Report

Date: 2026-05-23
Repository: `memory-lancedb-pro`
Artifact path: `docs/openclaw-dream-retrofit-report.md`

No explicit output artifact path was present in the environment or repository metadata, so this report is written as a versioned repo artifact under `docs/`.

## Phase Status

| Phase | Status | Notes |
|---|---|---|
| Phase 0: memory capability bootstrap | Complete | Registered `api.registerMemoryCapability` with `runtime`, `promptBuilder`, `flushPlanResolver`, and `publicArtifacts`; retained legacy runtime fallback. |
| Phase 1: runtime contract parity | Complete | Added `MemorySearchManager`-compatible runtime returning grounded `path/startLine/endLine/snippet/source/citation` results and scoped retrieval. |
| Phase 2: canonical corpus + indexer foundation | Complete | Added canonical file/session/dream corpus discovery and LanceDB upsert indexing while keeping files as source of truth. |
| Phase 3: dreaming compatibility glue | Complete | Exposed dream reports and `memory/.dreams/events.jsonl` public artifacts; expanded dreaming schema to match memory-core execution/source config. |
| Phase 4/5: hardening and docs | Complete for this pass | Added focused tests, CI manifest coverage, README/playbook updates, and ran the core regression group. |

## Implemented Outcomes

1. `memory-lancedb-pro` now registers `api.registerMemoryCapability` with:
   - `runtime`
   - `promptBuilder`
   - `flushPlanResolver`
   - `publicArtifacts`
2. Plugin config schema now supports:
   - `canonicalCorpus`
   - `dreaming.enabled/frequency/model/timezone/storage/execution/phases`
   - memory-core-compatible execution knobs: `speed`, `thinking`, `budget`, `maxOutputTokens`, `temperature`, `timeoutMs`
   - phase source enums for light/deep/rem dreaming.
3. Runtime search now returns grounded results with:
   - `path`
   - `startLine`
   - `endLine`
   - `snippet`
   - `source`
   - `citation`
4. Canonical corpus indexing now covers:
   - `MEMORY.md`
   - `memory/**/*.md`
   - `memory/dreaming/**/*.md`
   - recent `~/.openclaw/agents/<agentId>/sessions/*.jsonl`
5. LanceDB is used as the semantic index; canonical files remain source of truth.
6. Dream/public artifact compatibility includes:
   - dream reports as `dream-report`
   - `memory/.dreams/events.jsonl` as `event-log`
   - public artifacts for `MEMORY.md` and `memory/**/*.md`
7. Tests were added/updated for:
   - capability registration
   - runtime grounded results and source filtering
   - canonical corpus discovery/indexing/read fallback
   - dreaming schema compatibility
   - public artifact discovery
8. Docs were updated:
   - `README.md`
   - `docs/openclaw-integration-playbook.md`

## Changed Files

- `README.md`
- `docs/openclaw-integration-playbook.md`
- `index.ts`
- `openclaw.plugin.json`
- `package.json`
- `scripts/ci-test-manifest.mjs`
- `src/corpus-indexer.ts`
- `src/openclaw-memory-capability.ts`
- `src/store.ts`
- `test/corpus-indexer.test.mjs`
- `test/memory-capability-runtime.test.mjs`
- `test/plugin-manifest-regression.mjs`
- `docs/openclaw-dream-retrofit-report.md`

## Commits Created

- `25f3522 feat: bootstrap OpenClaw memory capability`
- `8259e9a feat: add OpenClaw memory runtime adapter`
- `9f010a4 feat: index canonical OpenClaw memory corpus`
- `304ac34 feat: expose dreaming compatibility artifacts`
- `7cefffd docs: document OpenClaw memory capability mode`
- `cdbf643 fix: harden canonical corpus indexing`
- `9e76b0c build: sync dist for OpenClaw runtime smoke`

## Backup Paths

Phase 0:

- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/index.ts.bak.20260523-105050`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/openclaw.plugin.json.bak.20260523-105050`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/plugin-manifest-regression.mjs.bak.20260523-105050`

Phase 1:

- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/openclaw-memory-capability.ts.bak.20260523-105543`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/index.ts.bak.20260523-105543`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/package.json.bak.20260523-105749`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/ci-test-manifest.mjs.bak.20260523-105749`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/plugin-manifest-regression.mjs.bak.20260523-105849`

Phase 2:

- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/store.ts.bak.20260523-110018`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/openclaw-memory-capability.ts.bak.20260523-110018`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/index.ts.bak.20260523-110018`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/package.json.bak.20260523-110018`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/ci-test-manifest.mjs.bak.20260523-110018`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/openclaw.plugin.json.bak.20260523-110119`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/plugin-manifest-regression.mjs.bak.20260523-110119`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/memory-capability-runtime.test.mjs.bak.20260523-110717`

Phase 3:

- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/openclaw-memory-capability.ts.bak.20260523-111545`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/memory-capability-runtime.test.mjs.bak.20260523-111545`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/openclaw.plugin.json.bak.20260523-111706`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/plugin-manifest-regression.mjs.bak.20260523-111706`

Phase 4/5:

- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/README.md.bak.20260523-111933`
- `/Users/choucheyu/.openclaw/workspace/memory/backups/2026-05-23/openclaw-integration-playbook.md.bak.20260523-111933`

## Checks Run

Baseline:

- `npm install` - completed.
- `npx tsc --noEmit` - passed.
- `node test/plugin-manifest-regression.mjs` - passed.

Phase 0:

- `npx tsc --noEmit` - passed.
- `node test/plugin-manifest-regression.mjs` - passed.

Phase 1:

- `npx tsc --noEmit` - passed.
- `node test/memory-capability-runtime.test.mjs` - passed.
- `node test/plugin-manifest-regression.mjs` - passed.
- `node scripts/verify-ci-test-manifest.mjs` - passed.

Phase 2:

- `npx tsc --noEmit` - passed.
- `node test/corpus-indexer.test.mjs` - passed.
- `node test/memory-capability-runtime.test.mjs` - passed.
- `node test/plugin-manifest-regression.mjs` - passed.
- `node scripts/verify-ci-test-manifest.mjs` - passed.
- `git diff --check` - passed.

Phase 3:

- `node -e "JSON.parse(require('node:fs').readFileSync('openclaw.plugin.json','utf8')); console.log('OK manifest JSON')"` - passed.
- `npx tsc --noEmit` - passed.
- `node test/plugin-manifest-regression.mjs` - passed.
- `node test/memory-capability-runtime.test.mjs` - passed.
- `git diff --check` - passed.

Phase 4/5:

- `npx tsc --noEmit` - passed.
- `node test/corpus-indexer.test.mjs` - passed.
- `node test/memory-capability-runtime.test.mjs` - passed.
- `node test/plugin-manifest-regression.mjs` - passed.
- `node scripts/verify-ci-test-manifest.mjs` - passed.
- `npm run test:core-regression` - passed.
- `git diff --check` - passed.

Live OpenClaw smoke:

- Isolated no-channel config validation - passed.
- Production gateway health check after smoke - passed.
- Production Telegram accounts health check after smoke - passed.
- Embedded OpenClaw agent memory tool registration - passed.
- `memory_store` + `memory_recall` smoke - passed.
- CRUD smoke (`memory_store`, `memory_recall`, `memory_update`, `memory_forget`) - passed.
- Public artifacts provider live listing against isolated workspace - passed.
- `/dreaming` plugin command registration in isolated runtime inventory - passed.
- `/dreaming status` handler smoke - passed.
- Real Telegram inbound `/dreaming status` dispatch - passed.
- `node --test test/recall-text-cleanup.test.mjs test/auto-recall-query-length.test.mjs test/auto-recall-timeout.test.mjs test/per-agent-auto-recall.test.mjs test/auto-capture-cleanup.test.mjs test/governance-metadata.test.mjs test/plugin-manifest-regression.mjs` - passed.

## Live Integration Smoke Matrix

| Surface | Status | Evidence |
|---|---|---|
| Isolated OpenClaw config | Pass | `openclaw.nochannels.dreaming.test.json` and `openclaw.nochannels.autohooks.test.json` validated with `valid: true`. |
| Production safety | Pass | Only production gateway on `18789` remained after smoke; no `19889`, `smoke-dreaming`, or `openclaw agent` leftovers. |
| Production health | Pass | Gateway health returned `ok: true`; Telegram accounts were `running: true`, `connected: true`, `lastError: null`. |
| Tool registration | Pass | Embedded agent prompt report exposed `memory_recall`, `memory_store`, `memory_forget`, `memory_update`. |
| Store/recall | Pass | Agent smoke returned `stored: true`, `recalled: true`. |
| Update/forget | Pass | CRUD smoke returned `stored/recalledOriginal/updated/recalledUpdated/forgotten/absentAfterForget: true`. |
| Public artifacts | Pass | Isolated workspace listed `MEMORY.md` as `memory-root` and `memory/2026-05-23.md` as `daily-note`. |
| `/dreaming` registration | Pass | Isolated runtime registered `/dreaming` from `memory-core` and created the managed dreaming cron sidecar. |
| `/dreaming status` handler | Pass | Handler returned enabled status, `0 3 * * *` cadence, and promotion policy. |
| Auto-recall logic | Pass | Targeted regression tests passed, including cleanup, governance, timeout, query length, per-agent policy, and before-prompt hook behavior. |
| Auto-capture cleanup/schema | Pass | Targeted auto-capture cleanup and plugin-manifest tests passed. |
| Real inbound slash dispatch | Pass | Telegram inbound `/dreaming status` produced bot reply message `36015`: enabled on, cadence `0 3 * * *`, promotion policy `score>=0.8, recalls>=3, uniqueQueries>=3`; Gateway log also recorded `telegram sendMessage ok ... message=36015`. |
| Embedded CLI prompt injection | Not proven | `openclaw agent --local` exposes memory tools, but did not demonstrate `before_prompt_build` auto-recall injection in the live smoke path. |

## Reviewer Notes

- The runtime adapter keeps existing `memory_recall`/tool behavior intact and adds OpenClaw memory-host runtime compatibility.
- Canonical corpus entries are stored with `openclaw_corpus` metadata and deterministic `corpus:<hash>` ids, so repeated syncs update the semantic index entry for the same canonical artifact.
- `MemoryStore.upsert()` was added as a serialized delete/add path because LanceDB is used as an index over canonical artifacts, not the canonical source.
- Session transcript parsing is deliberately conservative and best-effort: JSONL role/content blocks are rendered into searchable text and exposed as `source: "sessions"`.
- Public artifact behavior now matches memory-core's important surfaces: `MEMORY.md`, markdown memory files, dream reports, and the dreaming event log.

## Remaining Risks And Edge Cases

- A full real inbound `/dreaming status` dispatch was later executed through Telegram and passed. OpenClaw 2026.5.18 still exposes only command inventory through `commands.list` over CLI/RPC, not a direct CLI/RPC command execution method; therefore the verified path is real provider inbound dispatch, not a synthetic Gateway RPC call.
- `openclaw agent --local --message "/dreaming status"` is not a valid slash-command smoke because plugin slash commands are Gateway command-dispatch features, not ordinary agent prompt text.
- Embedded `openclaw agent --local` successfully exposes and runs memory tools, but did not prove live `before_prompt_build` auto-recall prompt injection. Auto-recall behavior remains covered by targeted regression tests.
- Canonical corpus sync is opportunistic through runtime search/sync, not a filesystem watcher.
- Session transcript classification does not yet reuse OpenClaw's internal dreaming/cron transcript classifier; users can disable transcript indexing with `canonicalCorpus.includeSessionTranscripts: false` if needed.
- Full `npm test` was not run because the relevant broader gate is `npm run test:core-regression`; focused gates and core regressions passed.

---

### Openclaw Integration Playbook (`docs/openclaw-integration-playbook.md`)

# OpenClaw Integration Playbook

This guide turns the integration and test findings for `memory-lancedb-pro` into a reusable checklist for two audiences:

- OpenClaw users who want a stable first deployment
- Maintainers who need a practical regression guide for future iterations

It is intentionally generic. It focuses on verifiable behavior, failure signatures, and operating rules instead of local machine paths.

## 0. Start with the correct user path

Before following the rest of this guide, classify your current state:

- new OpenClaw user or first-time memory setup
- existing OpenClaw user adding this plugin later
- existing `memory-lancedb-pro` user upgrading from a pre-v1.1.0 release

The operating rule is:

- `upgrade` is for older `memory-lancedb-pro` data
- `migrate` is for built-in `memory-lancedb` data
- `reembed` is for embedding rebuilds, not routine upgrades

If you mix those paths, debugging becomes much harder because storage-format issues and retrieval-quality issues get conflated.

## 1. Recommended Deployment Modes

Choose one of these modes explicitly.

### Mode A: Retrieval-first memory

Use this when you want:

- `memory_store` / `memory_recall`
- hybrid search (`vector + BM25`)
- auto-capture / auto-recall
- smart extraction and lifecycle ranking

Keep plugin session summaries disabled unless you have a concrete retrieval need for prior sessions.

### Mode B: Retrieval + session-summary search

Use this when you also want `/new` to write a searchable session summary into LanceDB.

In this mode:

- set plugin `sessionStrategy: "memoryReflection"` for LLM-powered reflection (structured extraction, dedup, lifecycle scoring)
- decide whether OpenClaw built-in `session-memory` should also remain enabled

> **Note:** The legacy `sessionMemory.enabled = true` maps to `systemSessionMemory` (simple session summary stored directly in LanceDB), not `memoryReflection`. If you need the full reflection pipeline, use `sessionStrategy` explicitly.

If both are enabled, `/new` can produce two outputs:

- built-in workspace/session summary files
- LanceDB session-summary memories written by `memory-lancedb-pro`

That is valid, but it is a double-write design. If you do not want duplicated session summarization paths, keep only one.

### Mode C: Retrieval + OpenClaw memory runtime and dreaming

Use this when `memory-lancedb-pro` owns `plugins.slots.memory` and OpenClaw should treat the plugin as the active memory capability.

In this mode:

- the plugin registers `runtime`, `promptBuilder`, `flushPlanResolver`, and `publicArtifacts` through `api.registerMemoryCapability`
- `canonicalCorpus.enabled` indexes `MEMORY.md`, `memory/**/*.md`, recent session JSONL transcripts, and `memory/dreaming/**/*.md` into LanceDB
- the files remain the source of truth; LanceDB is the semantic index for grounded search
- runtime search results must include `path`, `startLine`, `endLine`, `snippet`, `source`, and `citation`
- dream reports are exposed as `dream-report` public artifacts, and `memory/.dreams/events.jsonl` is exposed as the dreaming event log

If `dreaming.enabled` is true, OpenClaw may load the built-in memory-core dreaming sidecar while keeping `memory-lancedb-pro` as the selected memory slot owner. Keep the dreaming config under the `memory-lancedb-pro` plugin entry so OpenClaw resolves the sidecar against the active memory plugin.

## 2. Recommended Session Memory Strategy

For most users, use one of these patterns.

### Option 1: Disabled (no plugin session hooks)

Choose this when you mainly want transcript persistence and workspace summaries without plugin involvement.

- plugin `sessionStrategy: "none"` (or omit `sessionStrategy` entirely)
- OpenClaw built-in `hooks.internal.entries.session-memory.enabled = true`

### Option 2: Plugin only (LLM reflection)

Choose this when you want session summaries to participate in LanceDB retrieval with LLM-powered structured extraction, dedup, and lifecycle scoring.

- plugin `sessionStrategy: "memoryReflection"`
- OpenClaw built-in `hooks.internal.entries.session-memory.enabled = false`

### Option 3: Plugin only (simple summary)

Choose this when you want session summaries stored in LanceDB but do not need the full LLM reflection pipeline.

- plugin `sessionStrategy: "systemSessionMemory"`
- OpenClaw built-in `hooks.internal.entries.session-memory.enabled = false`

This writes a plain session summary directly into LanceDB as `fact` / `peripheral` tier entries, without LLM-driven dedup or lifecycle scoring.

### Option 4: Dual write

Choose this only if you explicitly want both:

- workspace markdown/session artifacts (via built-in session-memory)
- LanceDB-searchable session memories (via plugin `sessionStrategy`)

If you use dual write, document it for your team. Otherwise it will look like duplicate behavior during debugging.

### Strategy comparison

| Configuration | Strategy | Behavior |
|---|---|---|
| `sessionStrategy: "memoryReflection"` | memoryReflection | LLM-powered reflection: structured extraction, dedup, lifecycle scoring |
| `sessionStrategy: "systemSessionMemory"` | systemSessionMemory | Simple session summary stored directly in LanceDB as fact/peripheral |
| `sessionMemory: { enabled: true }` | systemSessionMemory | Legacy shorthand, same as `systemSessionMemory` above |
| `sessionStrategy: "none"` or omitted | none | Plugin session hooks disabled |

## 3. Baseline Verification Checklist

Run this before debugging retrieval quality.

```bash
openclaw config validate
openclaw status
openclaw gateway status
openclaw plugins info memory-lancedb-pro
openclaw hooks list --json
```

Confirm:

- plugin is loaded from the expected path
- `plugins.slots.memory` points to `memory-lancedb-pro`
- the plugin registers a memory capability, not only legacy hooks/tools
- the expected hooks are enabled
- the gateway has been restarted after config changes
- for multi-agent or multi-process write tests, the deployment matches the
  [lock-management requirements](lock-management.md)

If you use plugin session memory, `openclaw hooks list --json` should show the plugin hook:

- `memory-lancedb-pro-session-memory`

If you want plugin-only session summaries, also confirm:

- built-in `session-memory` is disabled

## 4. Fresh-Agent Bootstrap Checks

When a newly created agent fails on its first real turn, do not start with retrieval debugging. Verify agent bootstrap first.

Typical symptom:

- `Unknown model: openai-codex/gpt-5.4`

Common root cause:

- the new agent has not been initialized with agent-local model/auth indexes

Verify these files exist under the new agent directory:

- `~/.openclaw/agents/<agentId>/agent/models.json`
- `~/.openclaw/agents/<agentId>/agent/auth-profiles.json`

Also verify the agent can resolve at least one usable model before testing memory behavior.

Practical rule:

- if a fresh agent cannot complete a plain text turn, memory tests on that agent are not meaningful yet

## 5. Retrieval Quality Rules

### Short CJK keyword queries need explicit validation

Short Chinese keywords are a common false-negative case in hybrid retrieval systems.

Observed pattern during testing:

- unique identifiers or full sentences recall well
- short CJK keywords may depend on BM25/lexical matching quality
- lowering `minScore` can improve recall but often increases noise

Recommended tuning order:

1. verify BM25 / lexical fallback behavior first
2. verify hybrid fusion behavior second
3. tune `minScore` / `hardMinScore` last

Do not start by aggressively lowering thresholds. That often converts a retrieval-quality problem into a relevance-noise problem.

### Lifecycle scoring must not suppress strong fresh hits

If you use lifecycle decay and tiering:

- apply relevance filtering before lifecycle/time decay demotion
- ensure new `working` memories are not pushed below `hardMinScore` simply because decay was applied too early

Regression target:

- a fresh, high-relevance working-tier memory must remain retrievable

## 6. Functional Smoke Tests

These tests cover the core closed loop.

### CLI and storage

```bash
openclaw memory-pro stats
openclaw memory-pro list --scope global --limit 5
openclaw memory-pro search "your test keyword" --scope global --limit 5
```

Validate:

- stats returns usable counts
- list returns stored items in the expected scope
- search returns deterministic results for at least one exact identifier and one natural-language query

### Tool loop

Validate at least once:

- `memory_store`
- `memory_recall`
- `memory_update`
- `memory_forget`
- `memory_list`
- `memory_stats`

### OpenClaw memory runtime loop

When `canonicalCorpus.enabled` is true, create a temporary markdown note under `memory/YYYY-MM-DD.md` and verify runtime memory search can return it with:

- `source: "memory"`
- a real workspace-relative path, not only a `memory-lancedb-pro/<id>.md` virtual path
- line numbers and a snippet that match the file

For session transcript coverage, verify a recent `~/.openclaw/agents/<agentId>/sessions/*.jsonl` entry can be returned with `source: "sessions"` when session transcript indexing is enabled.

For dreaming coverage, verify that `memory/dreaming/*.md` appears as a `dream-report` public artifact and that `memory/.dreams/events.jsonl` appears as an `event-log` public artifact when present.

### Public X/Twitter source capture

Use this optional recipe when an OpenClaw agent gathers public X/Twitter context through TweetClaw and you want a reviewed source summary to become searchable memory.

Allowed source actions:

- search public tweets or replies
- look up a public user profile
- export public follower or media metadata when the workflow needs it
- consume monitors or webhooks that report public tweet events

Before calling `memory_store`, review the source result and keep only durable research context:

- tweet URL
- author handle
- published timestamp
- short text excerpt or human-written summary
- relevant metrics, if needed for the research task
- query or monitor that found the source
- capture date

Store the reviewed summary as `category: "fact"` for source evidence or `category: "decision"` when the team has made a decision from that evidence. Include the public source URL and capture date in the memory text or metadata so later recall can cite where the information came from.

Never store secrets or private account material from this flow:

- API keys, cookies, bearer tokens, or account credentials
- direct-message content
- draft post or reply text
- non-public profile, follower, or media data

Keep visible X/Twitter actions outside the memory plugin. Posting tweets, posting replies, sending direct messages, uploading media, and downloading media require explicit user approval in the tool workflow that performs the action. `memory-lancedb-pro` should only persist the reviewed summary after that workflow has produced safe public source context.

Verification checklist:

- the stored memory contains the public URL and capture date
- the text is a summary or short excerpt, not a raw transcript dump
- no credentials, cookies, direct messages, or draft text appear in the memory
- any visible action on X/Twitter was approved outside the memory plugin

### Scope isolation

Validate at least:

- `main -> main` hits
- `main -> work` miss
- `work -> global` allowed if intended
- `life -> work` miss unless explicitly permitted

### Smart extraction stability

Validate the three semantic branches:

- `create`
- `merge`
- `skip`

Then run a multi-turn sequence such as:

- `create -> skip -> merge -> skip`

Expected result:

- one stable memory record
- duplicates suppressed
- new facts merged without unbounded duplication

## 7. Real `/new` Session Test

If plugin session memory is enabled, run one real `/new` validation after the basic smoke tests.

Check for three facts:

1. the active session changes
2. the expected hook fires
3. the summary lands in the intended storage path

Plugin evidence should look like:

- a log entry that the session summary was stored for the previous session

If built-in session memory is also enabled, you should additionally see the built-in workspace/session artifact.

If built-in session memory is disabled, do not treat the absence of a workspace session-summary markdown file as a plugin failure.

## 8. Recommended Regression Matrix

Run this matrix before release candidates or after major retrieval changes.

### Integration

- plugin loads successfully
- gateway restart preserves plugin registration
- `hooks list` shows expected hook state

### Retrieval

- exact identifier recall
- short CJK keyword recall
- full sentence semantic recall
- rerank fallback behavior when reranker is unavailable

### Memory lifecycle

- fresh `working` memory remains retrievable
- tier promotion/demotion does not erase useful recall

### Extraction

- `create`
- `merge`
- `skip`
- multi-turn duplicate suppression

### Session flow

- `/new` triggers the intended hook path
- plugin-only mode does not rely on built-in session artifacts
- dual-write mode is explicit and understood

### Canonical corpus and dreaming

- `MEMORY.md` and `memory/**/*.md` stay readable as files
- LanceDB contains indexed corpus entries with `openclaw_corpus` metadata
- session transcript indexing can be disabled independently with `canonicalCorpus.includeSessionTranscripts`
- dreaming config validates under the `memory-lancedb-pro` plugin entry
- public artifacts include dream reports and the dreaming event log when files exist

### Agent bootstrap

- newly added agent can complete its first turn
- new agent has usable model/auth indexes

## 9. Troubleshooting Patterns

### Search returns empty, but the store contains data

Check in this order:

1. scope mismatch
2. `minScore` / `hardMinScore`
3. BM25 / lexical fallback availability
4. rerank endpoint health
5. lifecycle decay ordering

### `/new` appears to do nothing

Check:

- plugin `sessionStrategy` is set to `"memoryReflection"` or `"systemSessionMemory"` (not `"none"`)
- plugin hook is actually registered and named
- gateway has been restarted after the hook/config change
- built-in hook state matches your intended design

### Results became noisy after tuning

Likely cause:

- threshold reduction solved recall at the cost of precision

Preferred fix order:

1. lexical/BM25 improvement
2. hybrid-fusion tuning
3. rerank tuning
4. threshold change

## 10. Upgrade and Maintenance Notes

If you maintain local patches against the OpenClaw installation itself, treat them as temporary operational fixes, not durable plugin behavior.

After any `openclaw update`, re-check:

- fresh-agent bootstrap
- model provider resolution for newly added agents
- hook registration state
- `/new` behavior

If your team depends on local installation patches, keep them in a repeatable patch or automation script outside the plugin repo.

## 11. Recommended Documentation Policy for Future Changes

When changing retrieval or hook behavior, update all three artifacts together:

- user-facing README summary
- this integration playbook
- regression tests that prove the changed behavior

That keeps operational guidance aligned with actual behavior and prevents README drift.

---

### Openclaw Integration Playbook.Zh CN (`docs/openclaw-integration-playbook.zh-CN.md`)

# OpenClaw 集成与迭代手册

这份文档把 `memory-lancedb-pro` 在真实 OpenClaw 环境中的集成、联调和回归测试经验整理成一套可复用说明，面向两类读者：

- 初次接入 OpenClaw 的新用户
- 后续继续迭代检索、Hook、生命周期逻辑的维护者

目标不是记录某一台机器的细节，而是沉淀可验证的行为、常见故障特征和维护规则。

## 0. 先选对自己的使用路径

在继续看后面的章节前，先判断你目前属于哪一类：

- 新 OpenClaw 用户，或第一次接入记忆能力
- 已经用了 OpenClaw 一段时间，现在再接入本插件
- 已经在用旧版 `memory-lancedb-pro`，准备从 v1.1.0 之前升级

必须先分清三条命令的用途：

- `upgrade`：针对旧版 `memory-lancedb-pro` 数据
- `migrate`：针对内置 `memory-lancedb` 数据
- `reembed`：针对向量重建，不是常规升级步骤

如果把这三条路径混在一起，后续很容易把“数据格式问题”和“检索质量问题”混为一谈。

## 1. 推荐部署模式

建议先明确采用哪一种模式，再开始调参。

### 模式 A：以检索为主

适用场景：

- 使用 `memory_store` / `memory_recall`
- 需要混合检索（`vector + BM25`）
- 开启 auto-capture / auto-recall
- 使用 smart extraction 和 lifecycle ranking

这类场景下，除非你确实需要“把旧 session 摘要也作为可检索记忆”，否则不建议默认开启插件侧 session summary。

### 模式 B：检索 + session summary 可检索

适用场景：

- 除常规长期记忆外，还希望 `/new` 时把上一个 session 摘要写入 LanceDB，参与后续检索

这时需要：

- 设置插件 `sessionStrategy: "memoryReflection"`（LLM 结构化反思：去重、错误追踪、lifecycle 评分）
- 明确决定是否保留 OpenClaw 内置 `session-memory`

> **注意：** 旧版 `sessionMemory.enabled = true` 实际映射为 `systemSessionMemory`（简单摘要直存 LanceDB），而非 `memoryReflection`。如需完整反思管线，请显式使用 `sessionStrategy`。

如果两者同时开启，`/new` 可能产生两类结果：

- OpenClaw 内置的 workspace/session 摘要文件
- `memory-lancedb-pro` 写入 LanceDB 的 session-summary 记忆

这不是错误，但属于双写设计。若不希望排障时出现“看起来重复”的现象，应只保留一种路径。

## 2. Session Memory 选型建议

大部分场景推荐三选一，而不是默认双开。

### 方案 1：禁用（不启用插件 session hook）

适合：

- 主要想保留会话摘要文件
- 不要求 session summary 参与 LanceDB 检索

建议配置：

- 插件 `sessionStrategy: "none"`（或不设置 `sessionStrategy`）
- OpenClaw `hooks.internal.entries.session-memory.enabled = true`

### 方案 2：只用插件（LLM 反思模式）

适合：

- 希望 session summary 进入 LanceDB
- 需要 LLM 结构化提取、去重、lifecycle 评分

建议配置：

- 插件 `sessionStrategy: "memoryReflection"`
- OpenClaw `hooks.internal.entries.session-memory.enabled = false`

### 方案 3：只用插件（简单摘要模式）

适合：

- 希望 session summary 存入 LanceDB
- 不需要 LLM 反思管线

建议配置：

- 插件 `sessionStrategy: "systemSessionMemory"`
- OpenClaw `hooks.internal.entries.session-memory.enabled = false`

此模式将会话摘要直接写入 LanceDB，存为 `fact` / `peripheral` 层级条目，不经过 LLM 去重或 lifecycle 评分。

### 方案 4：双写

只在你明确需要以下两类产物时使用：

- workspace 中的摘要文件（通过内置 session-memory）
- LanceDB 中可检索的 session 记忆（通过插件 `sessionStrategy`）

若采用双写，建议在团队文档中写清楚，否则后续维护者容易把它误判成重复存储问题。

### 策略对照表

| 配置方式 | 策略 | 行为 |
|---|---|---|
| `sessionStrategy: "memoryReflection"` | memoryReflection | LLM 结构化反思：提取、去重、lifecycle 评分 |
| `sessionStrategy: "systemSessionMemory"` | systemSessionMemory | 简单会话摘要直存 LanceDB（fact/peripheral 层级） |
| `sessionMemory: { enabled: true }` | systemSessionMemory | 旧版简写，等同于上面的 `systemSessionMemory` |
| `sessionStrategy: "none"` 或不设置 | none | 插件 session hook 不启用 |

## 3. 基线检查清单

在开始调检索质量之前，先确认基础集成是通的。

```bash
openclaw config validate
openclaw status
openclaw gateway status
openclaw plugins info memory-lancedb-pro
openclaw hooks list --json
```

至少确认：

- 插件从预期路径加载
- `plugins.slots.memory` 指向 `memory-lancedb-pro`
- 预期 Hook 处于启用状态
- 改完配置后 Gateway 已重启

如果启用了插件侧 session memory，`openclaw hooks list --json` 里应能看到：

- `memory-lancedb-pro-session-memory`

如果目标是插件单写模式，还应同时确认：

- 内置 `session-memory` 已禁用

## 4. 新 Agent 引导检查

新 Agent 首轮真实对话失败时，不要先怀疑检索。先检查 Agent 启动链路。

典型症状：

- `Unknown model: openai-codex/gpt-5.4`

常见根因：

- 新 Agent 没有初始化本地模型/认证索引

建议检查以下文件是否存在：

- `~/.openclaw/agents/<agentId>/agent/models.json`
- `~/.openclaw/agents/<agentId>/agent/auth-profiles.json`

并确认该 Agent 至少能先跑通一次普通文本对话，再做记忆相关测试。

工程上可以直接采用这条规则：

- 如果新 Agent 连普通 turn 都跑不通，那么该 Agent 上的 memory 测试结论没有意义

## 5. 检索质量规则

### 短中文关键词必须单独验证

短中文词是混合检索里最常见的假阴性场景之一。

实测经验通常是：

- 唯一代号、完整句子更容易召回
- 短中文词更依赖 BM25 / 词法匹配质量
- 直接降低 `minScore` 能提召回，但常常同时带来明显噪声

推荐调优顺序：

1. 先确认 BM25 / 词法兜底是否有效
2. 再确认 hybrid fusion 是否把强词法命中保留下来
3. 最后再调 `minScore` / `hardMinScore`

不要把“先大幅降阈值”当成默认修复方案。那通常只是把“召回问题”换成了“精度问题”。

### 生命周期排序不能误杀高相关新记忆

如果启用了 lifecycle decay 和 tiering，需要保证：

- 先按相关性裁剪，再施加 lifecycle/time decay
- 新写入的 `working` 记忆不会因为衰减顺序不当，被压到 `hardMinScore` 以下

建议把下面这条作为回归目标：

- 新鲜且高相关的 `working` 记忆必须可召回

## 6. 功能烟测清单

以下检查能覆盖主要闭环。

### CLI 与存储

```bash
openclaw memory-pro stats
openclaw memory-pro list --scope global --limit 5
openclaw memory-pro search "your test keyword" --scope global --limit 5
```

至少验证：

- `stats` 能返回统计信息
- `list` 能看到预期 scope 下的数据
- `search` 对“唯一标识符”和“自然语言查询”都至少有一个稳定命中

### 工具闭环

至少完整测一轮：

- `memory_store`
- `memory_recall`
- `memory_update`
- `memory_forget`
- `memory_list`
- `memory_stats`

### Scope 隔离

至少验证以下方向：

- `main -> main` 命中
- `main -> work` 不命中
- `work -> global` 若设计允许，应命中
- `life -> work` 除非显式授权，否则不命中

### Smart Extraction 稳定性

至少验证三种语义分支：

- `create`
- `merge`
- `skip`

然后再做一组多轮序列：

- `create -> skip -> merge -> skip`

预期结果：

- 库里仍只有一条稳定记忆
- 重复内容被抑制
- 新信息被合并，而不是无限新增重复条目

## 7. 真实 `/new` 会话测试

如果启用了插件侧 session memory，基础烟测后应再做一次真实 `/new` 验证。

重点确认三件事：

1. 活跃 session 确实切换
2. 预期 Hook 确实触发
3. 摘要写到了你期望的存储路径

插件侧的有效证据应表现为：

- 日志里出现“已为上一会话保存 session summary”的记录

如果同时保留内置 session-memory，还应看到内置 workspace/session 摘要产物。

如果内置 session-memory 已禁用，那么“没有看到 workspace session-summary markdown”不能直接判定为插件失败。

## 8. 推荐回归矩阵

每次准备发布、或修改检索 / Hook / 生命周期逻辑后，建议至少跑下面这组回归。

### 集成

- 插件可正常加载
- Gateway 重启后 Hook 注册状态不丢失
- `hooks list` 与预期一致

### 检索

- 唯一标识符召回
- 短中文关键词召回
- 完整句子语义召回
- rerank 服务不可用时的降级路径

### 生命周期

- fresh `working` 记忆仍能召回
- tier 升降不会导致有用记忆被过早过滤

### 提取

- `create`
- `merge`
- `skip`
- 多轮重复抑制

### Session 流程

- `/new` 触发到预期 Hook
- plugin-only 模式不依赖内置 session 文件
- dual-write 模式是显式设计，而不是误配

### Agent 引导

- 新增 Agent 首轮真实对话可成功
- 新 Agent 模型/认证索引完整

## 9. 常见排障模式

### search 返回空，但库里明明有数据

按这个顺序排查：

1. scope 是否错了
2. `minScore` / `hardMinScore` 是否过高
3. BM25 / 词法兜底是否有效
4. rerank endpoint 是否可用
5. lifecycle decay 排序顺序是否合理

### `/new` 看起来没生效

优先检查：

- 插件 `sessionStrategy` 是否设为 `"memoryReflection"` 或 `"systemSessionMemory"`（而非 `"none"`）
- 插件 Hook 是否真的注册成功且有名字
- 改完配置后 Gateway 是否已重启
- 内置 Hook 状态是否符合当前设计

### 调参后召回上去了，但噪声变多

这通常意味着：

- 你用降阈值解决了召回，但牺牲了精度

优先修复顺序建议是：

1. 词法 / BM25 能力
2. hybrid fusion 规则
3. rerank 策略
4. 阈值调整

## 10. 升级与维护说明

如果你对 OpenClaw 安装本体做过本地补丁，请把它视为运行时修复，而不是插件自身的长期保证。

每次执行 `openclaw update` 之后，至少重新检查：

- 新 Agent 引导是否正常
- 新 Agent 的模型 provider 是否能解析
- Hook 注册状态是否仍正确
- `/new` 行为是否符合预期

如果团队依赖这些安装侧补丁，建议把补丁过程整理成独立 patch 或脚本，不要只依赖手工修改。

## 11. 后续文档维护建议

以后凡是修改以下行为之一：

- 检索逻辑
- Hook 注册方式
- 生命周期排序
- session summary 路径

建议同步更新三处内容：

- README 中的用户级摘要
- 本文档
- 对应回归测试

这样可以避免“实现已变，但文档和测试还停留在旧行为”的漂移问题。

---

### Release Checklist (`docs/release-checklist.md`)

# Release Checklist

Use this checklist before publishing a new `memory-lancedb-pro` package. It is
intended for the release tracked in #812 and future beta/stable cuts.

## Release Target

- Current package version: `1.1.0-beta.11`
- Recommended first publish: beta dist-tag
- Stable channel decision: maintainer-owned after beta smoke testing

The current release driver is that npm users are still behind repository
`master`: merged fixes are not available from the published `latest` package,
and the `beta` dist-tag still points at an older beta package.

## Preflight

Run these from a clean checkout:

```bash
npm ci
npm run test:packaging-and-workflow
npm run build
npm pack --dry-run
```

Confirm:

- `package.json` and `openclaw.plugin.json` versions match
- `package.json main` points at `dist/index.js`
- `package.json openclaw.extensions` points at `./dist/index.js`
- `package.json files` includes `dist/**/*`
- `CHANGELOG.md` and `CHANGELOG-v1.1.0.md` start with the package version
- `npm pack --dry-run` includes compiled `dist` output and excludes test files

## Publish Dry Run

```bash
npm publish --tag beta --dry-run
```

Review the file list and package metadata before publishing.

## Publish

```bash
npm publish --tag beta
```

After publish, verify the public registry state:

```bash
npm view memory-lancedb-pro dist-tags version versions --json
npm view memory-lancedb-pro@beta version main openclaw files --json
```

The `beta` dist-tag should point at the newly published version, and the package
runtime entries should point at compiled JavaScript under `dist/`.

## Post-Publish Smoke

On a machine with a current OpenClaw install:

```bash
openclaw plugins registry --refresh
openclaw plugins install memory-lancedb-pro@beta
openclaw plugins doctor
```

Confirm OpenClaw installs the package without falling back to TypeScript source
entrypoints.

---

### Notes Reflection Write Path Findings (`docs/notes/reflection-write-path-findings.md`)

# Reflection write-path findings

Findings from a code-plus-trace audit of the reflection persistence paths.
Kept here as discussion material; each item states what the code does today
and what remains open.

## Two writers, different gates

The reflection distiller output is persisted by two independent writers:

1. **Writer 1 (mapped rows).** Four headings of the reflection markdown
   (User model deltas, Agent model deltas, Lessons & pitfalls, Decisions)
   are parsed into individual rows (`extractInjectableReflectionMappedMemoryItems`)
   and bulk-stored as ordinary memories with `metadata.type =
   "memory-reflection-mapped"`. Until the accompanying change, this path had
   no admission gate and no gating knob; `reflectionStoreToLanceDB` gates
   only writer 2. Mapped rows now route through the same AdmissionController
   as extraction candidates when admission control is enabled (passthrough
   when disabled, fail-open on infra errors). Scoring category mapping:
   `preference` (user/agent model deltas) scores as `preferences`, `fact`
   (lessons) as `cases`, `decision` as `events`.

2. **Writer 2 (reflection documents).** `storeReflectionToLanceDB` persists
   sliced reflection documents under category `reflection`, gated by the
   `reflectionStoreToLanceDB` config knob. Unchanged by this work.

## Dead reader

`loadReflectionMappedRowsFromEntries` (reflection-store) was built to read
mapped rows back for a dedicated injection path, but it has no production
callers (only a test imports it). The only living read path for mapped rows
is generic semantic recall. A code comment now marks this. Open question:
either wire the dedicated reader or consider retiring mapped rows in favor
of writer 2 plus recall.

## Cross-writer double-store

"Decisions (durable)" bullets can be persisted twice: once by writer 1 as a
`decision` row, and once inside writer 2's document slices (invariant
fallback). There is no cross-lane dedup between the two writers. Any future
dedup would need to compare across `metadata.type` values.

## memory_layer parity question

The auto-recall governance filter drops entries with `memory_layer` in
`{archive, reflection}`, but only the dreaming engine sets layer
`reflection`. Mapped rows carry layer semantics of ordinary confirmed
memories, so they pass recall filters that reflection-layer content does
not. If mapped rows are conceptually reflection output, they may deserve
the same layer tag; if they are conceptually ordinary memories, the
admission gate added here is the appropriate control. Left as a design
question.

## Auto-capture watermark

The per-session auto-capture cursor (`autoCaptureSeenTextCount`) is reset
to zero after every successful smart extraction, which makes the next
capture of the same session re-read the whole history instead of the
delta. Analysis and fix (recording the consumed length instead) are on the
dedicated watermark fix branch.

## Residual gap

The mapped-row admission gate reuses the SmartExtractor's controller
instance. When smart extraction is disabled while admission control is
enabled, mapped rows still pass ungated (no controller exists to borrow).
This mirrors the pre-change behavior and is called out here for
completeness.

---

--- METRICS ---
- Files Extracted: 11
- Estimated Token Budget: ~30327 tokens
- Canonical Reference: https://codewiki.google/github.com/CortexReach/memory-lancedb-pro
