{
  "id": "memory-lancedb-pro",
  "name": "Memory (LanceDB Pro)",
  "description": "Enhanced LanceDB-backed long-term memory with hybrid retrieval, multi-scope isolation, long-context chunking, and management CLI",
  "version": "1.0.32",
  "kind": "memory",
  "configSchema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "embedding": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "provider": {
            "type": "string",
            "const": "openai-compatible"
          },
          "apiKey": {
            "oneOf": [
              {
                "type": "string",
                "minLength": 1
              },
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1
                },
                "minItems": 1
              }
            ],
            "description": "Single API key or array of keys for round-robin rotation"
          },
          "model": {
            "type": "string"
          },
          "baseURL": {
            "type": "string"
          },
          "dimensions": {
            "type": "integer",
            "minimum": 1
          },
          "taskQuery": {
            "type": "string",
            "description": "Embedding task for queries (provider-specific, e.g. Jina: retrieval.query)"
          },
          "taskPassage": {
            "type": "string",
            "description": "Embedding task for passages/documents (provider-specific, e.g. Jina: retrieval.passage)"
          },
          "normalized": {
            "type": "boolean",
            "description": "Request normalized embeddings when supported by the provider (e.g. Jina v5)"
          },
          "chunking": {
            "type": "boolean",
            "default": true,
            "description": "Enable automatic chunking for documents exceeding embedding context limits"
          }
        },
        "required": [
          "apiKey"
        ]
      },
      "dbPath": {
        "type": "string"
      },
      "enableManagementTools": {
        "type": "boolean",
        "default": false,
        "description": "Enable memory_list and memory_stats management tools"
      },
      "autoCapture": {
        "type": "boolean"
      },
      "autoRecall": {
        "type": "boolean",
        "default": false
      },
      "autoRecallMinLength": {
        "type": "integer",
        "minimum": 1,
        "maximum": 200,
        "default": 15,
        "description": "Minimum prompt length (in characters) to trigger auto-recall. Prompts shorter than this are skipped. Default: 15 for English, 6 for CJK."
      },
      "autoRecallMinRepeated": {
        "type": "integer",
        "minimum": 0,
        "maximum": 100,
        "default": 0,
        "description": "Minimum number of turns before the same memory can be recalled again in the same session. Set to 0 to disable deduplication (default behavior: inject all memories)."
      },
      "captureAssistant": {
        "type": "boolean"
      },
      "retrieval": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "hybrid",
              "vector"
            ],
            "default": "hybrid"
          },
          "vectorWeight": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "default": 0.7
          },
          "bm25Weight": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "default": 0.3
          },
          "minScore": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "default": 0.3
          },
          "rerank": {
            "type": "string",
            "enum": [
              "cross-encoder",
              "lightweight",
              "none"
            ],
            "default": "cross-encoder"
          },
          "rerankApiKey": {
            "type": "string",
            "description": "API key for reranker service (enables cross-encoder reranking)"
          },
          "rerankModel": {
            "type": "string",
            "default": "jina-reranker-v3",
            "description": "Reranker model name"
          },
          "rerankEndpoint": {
            "type": "string",
            "default": "https://api.jina.ai/v1/rerank",
            "description": "Reranker API endpoint URL. Compatible with Jina, SiliconFlow, Pinecone, or any service with a similar interface."
          },
          "rerankProvider": {
            "type": "string",
            "enum": [
              "jina",
              "siliconflow",
              "voyage",
              "pinecone"
            ],
            "default": "jina",
            "description": "Reranker provider format. Determines request/response shape and auth header."
          },
          "candidatePoolSize": {
            "type": "integer",
            "minimum": 10,
            "maximum": 100,
            "default": 20
          },
          "recencyHalfLifeDays": {
            "type": "number",
            "minimum": 0,
            "maximum": 365,
            "default": 14,
            "description": "Half-life in days for recency boost. Newer memories get higher scores. Set 0 to disable."
          },
          "recencyWeight": {
            "type": "number",
            "minimum": 0,
            "maximum": 0.5,
            "default": 0.1,
            "description": "Maximum recency boost factor added to score"
          },
          "filterNoise": {
            "type": "boolean",
            "default": true,
            "description": "Filter out noise memories (agent denials, meta-questions, boilerplate)"
          },
          "lengthNormAnchor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 5000,
            "default": 500,
            "description": "Length normalization anchor in chars. Entries longer than this get score penalized. Set 0 to disable."
          },
          "hardMinScore": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "default": 0.35,
            "description": "Hard cutoff after all scoring stages. Results below this score are discarded."
          },
          "timeDecayHalfLifeDays": {
            "type": "number",
            "minimum": 0,
            "maximum": 365,
            "default": 60,
            "description": "Time decay half-life in days. Old entries lose score gradually. Floor at 0.5x. Set 0 to disable."
          },
          "reinforcementFactor": {
            "type": "number",
            "minimum": 0,
            "maximum": 2,
            "default": 0.5,
            "description": "Access reinforcement factor for time decay. Frequently recalled memories decay slower. 0 to disable."
          },
          "maxHalfLifeMultiplier": {
            "type": "number",
            "minimum": 1,
            "maximum": 10,
            "default": 3,
            "description": "Maximum half-life multiplier from access reinforcement. Prevents frequently accessed memories from becoming immortal."
          }
        }
      },
      "sessionMemory": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "enabled": {
            "type": "boolean",
            "default": true,
            "description": "Store session summaries to LanceDB on /new command (replaces built-in session-memory hook)"
          },
          "messageCount": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 15,
            "description": "Number of recent messages to include in session summary"
          }
        }
      },
      "scopes": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "default": {
            "type": "string",
            "default": "global"
          },
          "definitions": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "description": {
                  "type": "string"
                }
              }
            }
          },
          "agentAccess": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "mdMirror": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "enabled": {
            "type": "boolean",
            "default": false,
            "description": "Enable dual-write: store memories in both LanceDB and human-readable Markdown files"
          },
          "dir": {
            "type": "string",
            "description": "Fallback directory for Markdown mirror files when agent workspace is unknown"
          }
        }
      }
    },
    "required": [
      "embedding"
    ]
  },
  "uiHints": {
    "embedding.apiKey": {
      "label": "API Key(s)",
      "sensitive": true,
      "placeholder": "sk-proj-... or [\"key1\", \"key2\"] for rotation",
      "help": "Single API key or array of keys for round-robin rotation with automatic failover on rate limits (or use ${OPENAI_API_KEY}; use a dummy value for keyless local endpoints)"
    },
    "embedding.model": {
      "label": "Embedding Model",
      "placeholder": "text-embedding-3-small",
      "help": "Embedding model name (e.g. text-embedding-3-small, gemini-embedding-001, nomic-embed-text)"
    },
    "embedding.baseURL": {
      "label": "Base URL",
      "placeholder": "https://api.openai.com/v1",
      "help": "Custom base URL for OpenAI-compatible embedding endpoints (e.g. https://generativelanguage.googleapis.com/v1beta/openai/ for Gemini, http://localhost:11434/v1 for Ollama)",
      "advanced": true
    },
    "embedding.dimensions": {
      "label": "Vector Dimensions",
      "placeholder": "auto-detected from model",
      "help": "Override vector dimensions for custom models not in the built-in lookup table",
      "advanced": true
    },
    "embedding.taskQuery": {
      "label": "Query Task",
      "placeholder": "retrieval.query",
      "help": "Optional task selector for query embeddings (Jina: retrieval.query). If unset, no task field is sent.",
      "advanced": true
    },
    "embedding.taskPassage": {
      "label": "Passage Task",
      "placeholder": "retrieval.passage",
      "help": "Optional task selector for passage/document embeddings (Jina: retrieval.passage). If unset, no task field is sent.",
      "advanced": true
    },
    "embedding.normalized": {
      "label": "Normalized Embeddings",
      "help": "Request normalized embeddings when the provider supports it (Jina v5). If unset, the field is not sent.",
      "advanced": true
    },
    "embedding.chunking": {
      "label": "Auto-Chunk Documents",
      "help": "Automatically split long documents into chunks when they exceed embedding context limits. Set to false to disable and let the model fail on long documents.",
      "advanced": true
    },
    "dbPath": {
      "label": "Database Path",
      "placeholder": "~/.openclaw/memory/lancedb-pro",
      "help": "Directory path for the LanceDB database files",
      "advanced": true
    },
    "autoCapture": {
      "label": "Auto-Capture",
      "help": "Automatically capture important information from conversations"
    },
    "autoRecall": {
      "label": "Auto-Recall",
      "help": "Automatically inject relevant memories into context"
    },
    "autoRecallMinLength": {
      "label": "Auto-Recall Min Length",
      "help": "Minimum prompt length to trigger auto-recall (shorter prompts are skipped). Default: 15 chars for English, 6 for CJK.",
      "advanced": true
    },
    "autoRecallMinRepeated": {
      "label": "Auto-Recall Min Repeated",
      "help": "Minimum number of conversation turns before a specific memory can be re-injected in the same session.",
      "advanced": true
    },
    "captureAssistant": {
      "label": "Capture Assistant Messages",
      "help": "Also auto-capture assistant messages (default false to reduce memory pollution)",
      "advanced": true
    },
    "retrieval.mode": {
      "label": "Retrieval Mode",
      "help": "Use hybrid search (vector + BM25) or vector-only for backward compatibility",
      "advanced": true
    },
    "retrieval.vectorWeight": {
      "label": "Vector Search Weight",
      "help": "Weight for vector similarity in hybrid search (0-1)",
      "advanced": true
    },
    "retrieval.bm25Weight": {
      "label": "BM25 Search Weight",
      "help": "Weight for BM25 keyword search in hybrid search (0-1)",
      "advanced": true
    },
    "retrieval.minScore": {
      "label": "Minimum Score Threshold",
      "help": "Drop results below this relevance score (0-1)",
      "advanced": true
    },
    "retrieval.rerank": {
      "label": "Reranking Mode",
      "help": "Re-score fused results for better quality (cross-encoder uses configured reranker API)",
      "advanced": true
    },
    "retrieval.rerankApiKey": {
      "label": "Reranker API Key",
      "sensitive": true,
      "placeholder": "jina_... / sk-... / pcsk_...",
      "help": "Reranker API key for cross-encoder reranking",
      "advanced": true
    },
    "retrieval.rerankModel": {
      "label": "Reranker Model",
      "placeholder": "jina-reranker-v3",
      "help": "Reranker model name (e.g. jina-reranker-v3, BAAI/bge-reranker-v2-m3)",
      "advanced": true
    },
    "retrieval.rerankEndpoint": {
      "label": "Reranker Endpoint",
      "placeholder": "https://api.jina.ai/v1/rerank",
      "help": "Custom reranker API endpoint URL",
      "advanced": true
    },
    "retrieval.rerankProvider": {
      "label": "Reranker Provider",
      "help": "Provider format: jina (default), siliconflow, voyage, or pinecone",
      "advanced": true
    },
    "retrieval.candidatePoolSize": {
      "label": "Candidate Pool Size",
      "help": "Number of candidates to fetch before fusion and reranking",
      "advanced": true
    },
    "retrieval.lengthNormAnchor": {
      "label": "Length Normalization Anchor",
      "help": "Entries longer than this (chars) get score penalized to prevent long entries dominating. 0 = disabled.",
      "advanced": true
    },
    "retrieval.hardMinScore": {
      "label": "Hard Minimum Score",
      "help": "Discard results below this score after all scoring stages. Higher = fewer but more relevant results.",
      "advanced": true
    },
    "retrieval.timeDecayHalfLifeDays": {
      "label": "Time Decay Half-Life",
      "help": "Old entries lose score over this many days. Floor at 0.5x. 0 = disabled.",
      "advanced": true
    },
    "sessionMemory.enabled": {
      "label": "Session Memory",
      "help": "Store session summaries to LanceDB when /new is triggered (replaces built-in session-memory hook)"
    },
    "sessionMemory.messageCount": {
      "label": "Session Message Count",
      "help": "Number of recent messages to include in session summaries",
      "advanced": true
    },
    "scopes.default": {
      "label": "Default Scope",
      "help": "Default memory scope for new memories",
      "advanced": true
    },
    "scopes.definitions": {
      "label": "Scope Definitions",
      "help": "Define custom memory scopes with descriptions",
      "advanced": true
    },
    "scopes.agentAccess": {
      "label": "Agent Access Control",
      "help": "Define which scopes each agent can access",
      "advanced": true
    },
    "enableManagementTools": {
      "label": "Management Tools",
      "help": "Enable memory_list and memory_stats tools for debugging and auditing",
      "advanced": true
    },
    "mdMirror.enabled": {
      "label": "Markdown Mirror",
      "help": "Write a human-readable Markdown copy alongside LanceDB storage (dual-write mode)"
    },
    "mdMirror.dir": {
      "label": "Mirror Fallback Directory",
      "help": "Fallback directory when agent workspace mapping is unavailable",
      "advanced": true
    }
  }
}
