neolata-mem

ClawSkills 作者 clawskills v0.8.4

Graph-native memory engine for AI agents — hybrid vector+keyword search, biological decay, Zettelkasten linking, trust-gated conflict resolution, explainability, episodes, compression & consolidation. Zero dependencies. npm install and go.

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install clawskills:clawskills~jeremiaheth-neolata-mem
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Aclawskills~jeremiaheth-neolata-mem/file -o jeremiaheth-neolata-mem.md
# neolata-mem — Agent Memory Engine

Graph-native memory for AI agents with hybrid search, biological decay, and zero infrastructure.

**npm package:** `@jeremiaheth/neolata-mem`
**Repository:** [github.com/Jeremiaheth/neolata-mem](https://github.com/Jeremiaheth/neolata-mem)
**License:** Elastic-2.0 | **Tests:** 367/367 passing (34 files) | **Node:** ≥18

## When to Use This Skill

Use neolata-mem when you need:
- **Persistent memory across sessions** that survives context compaction
- **Semantic search** over stored facts, decisions, and findings
- **Memory decay** so stale information naturally fades
- **Multi-agent memory** with cross-agent search and graph linking
- **Conflict resolution** — detect and evolve contradictory memories

Do NOT use if:
- You only need OpenClaw's built-in `memorySearch` (keyword + vector on workspace files)
- You want cloud-hosted memory (use Mem0 instead)
- You need a full knowledge graph database (use Graphiti + Neo4j)

## Install

```bash
npm install @jeremiaheth/neolata-mem
```

No Docker. No Python. No Neo4j. No cloud API required.

> **Supply-chain verification:** This package has zero runtime dependencies and no install scripts. Verify before installing:
> ```bash
> # Check for install scripts (should show only "test"):
> npm view @jeremiaheth/neolata-mem scripts
> # Check for runtime deps (should be empty):
> npm view @jeremiaheth/neolata-mem dependencies
> # Audit the tarball contents (15 files, ~40 kB):
> npm pack @jeremiaheth/neolata-mem --dry-run
> ```
> Source is fully auditable at [github.com/Jeremiaheth/neolata-mem](https://github.com/Jeremiaheth/neolata-mem).

## Security & Data Flow

**Default configuration is fully local** — JSON files on disk, no network calls, no embeddings, no external services.

Data only leaves the host if you **explicitly configure** one of these:

| Feature | What leaves | Where it goes | How to avoid |
|---------|------------|---------------|-------------|
| Embeddings (OpenAI/NVIDIA/Azure) | Memory text | Embedding API endpoint | Use `noop` embeddings or Ollama (local) |
| LLM (OpenAI/OpenClaw/Ollama) | Memory text for extraction/compression | LLM API endpoint | Don't configure `llm` option, or use Ollama |
| Supabase storage | All memory data | Your Supabase project | Use `json` or `memory` storage (default) |
| Webhook writethrough | Store/decay event payloads | Your webhook URL | Don't configure `webhookWritethrough` |

**Key security properties:**
- Only 2 env vars are read directly by code: `OPENAI_API_KEY` and `OPENCLAW_GATEWAY_TOKEN`. All others (Supabase, NVIDIA, Azure) are passed via explicit config objects.
- All provider URLs are validated against SSRF (private IPs blocked, cloud metadata blocked).
- Supabase: prefer anon key + RLS over service key. Service key bypasses row-level security.
- JSON storage uses atomic writes (temp file + rename) to prevent corruption.
- All user content sent to LLMs is XML-fenced with injection guards.
- Test safely with `storage: { type: 'memory' }` — nothing touches disk or network.

See `docs/guide.md § Security` for the full security model.

## Quick Start (Zero Config)

```javascript
import { createMemory } from '@jeremiaheth/neolata-mem';

const mem = createMemory();
await mem.store('agent-1', 'User prefers dark mode');
const results = await mem.search('agent-1', 'UI preferences');
```

Works immediately with local JSON storage and keyword search. No API keys needed.

## With Semantic Search

```javascript
const mem = createMemory({
  embeddings: {
    type: 'openai',
    apiKey: process.env.OPENAI_API_KEY,
    model: 'text-embedding-3-small',
  },
});

// Agent IDs like 'kuro' and 'maki' are just examples — use any string.
await mem.store('kuro', 'Found XSS in login form', { category: 'finding', importance: 0.9 });
const results = await mem.search('kuro', 'security vulnerabilities');
```

Supports **5+ embedding providers**: OpenAI, NVIDIA NIM, Ollama, Azure, Together, or any OpenAI-compatible endpoint.

## Key Features

### Hybrid Search (Vector + Keyword Fallback)
Uses semantic similarity when embeddings are configured; falls back to tokenized keyword matching when they're not:
```javascript
// With embeddings → vector cosine similarity search
// Without embeddings → normalized keyword matching (stop word removal, lowercase, dedup)
const results = await mem.search('agent', 'security vulnerabilities');
```

Keyword search uses an inverted token index for O(1) lookups. When >500 memories exist, vector search pre-filters candidates using token overlap before cosine similarity (candidate narrowing).

### Biological Decay
Memories fade over time unless reinforced. Old, unaccessed memories naturally lose relevance:
```javascript
await mem.decay();        // Run maintenance — archive/delete stale memories
await mem.reinforce(id);  // Boost a memory to resist decay
```

### Memory Graph (Zettelkasten Linking)
Every memory is automatically linked to related memories by semantic similarity:
```javascript
const links = await mem.links(memoryId);     // Direct connections
const path = await mem.path(idA, idB);       // Shortest path between memories
const clusters = await mem.clusters();        // Detect topic clusters
```

### Conflict Resolution & Quarantine
Detect contradictions before storing — with claim-based structural detection or LLM-based semantic detection:
```javascript
// Structural (no LLM needed): claim-based conflict detection
await mem.store('agent', 'Server uses port 443', {
  claim: { subject: 'server', predicate: 'port', value: '443' },
  provenance: { source: 'user_explicit', trust: 1.0 },
  onConflict: 'quarantine',  // low-trust conflicts quarantined for review
});

// Semantic (requires LLM): LLM classifies as conflict/update/novel
await mem.evolve('agent', 'Server now uses port 8080');

// Review quarantined memories
const quarantined = await mem.listQuarantined();
await mem.reviewQuarantine(quarantined[0].id, { action: 'activate' });
```

### Predicate Schema Registry
Define per-predicate rules for conflict handling, normalization, and deduplication:
```javascript
const mem = createMemory({
  predicateSchemas: {
    'preferred_language': { cardinality: 'single', conflictPolicy: 'supersede', normalize: 'lowercase_trim' },
    'spoken_languages':   { cardinality: 'multi', dedupPolicy: 'corroborate' },
    'salary':             { cardinality: 'single', conflictPolicy: 'require_review', normalize: 'currency' },
  },
});
```

Options: `cardinality` (single/multi), `conflictPolicy` (supersede/require_review/keep_both), `normalize` (none/trim/lowercase/lowercase_trim/currency), `dedupPolicy` (corroborate/store).

### Explainability API
Understand why search returned or filtered specific memories:
```javascript
const results = await mem.search('agent', 'query', { explain: true });
console.log(results.meta);        // query options, result count
console.log(results[0].explain);  // retrieved, rerank, statusFilter details

const detail = await mem.explainMemory(memoryId);
// { id, status, trust, confidence, provenance, claimSummary }
```

### Multi-Agent Support
```javascript
await mem.store('kuro', 'Vuln found in API gateway');
await mem.store('maki', 'API gateway deployed to prod');
const all = await mem.searchAll('API gateway');  // Cross-agent search
```

### Episodes (Temporal Grouping)
Group related memories into named episodes:
```javascript
const ep = await mem.createEpisode('Deploy v2.0', [id1, id2, id3], { tags: ['deploy'] });
const ep2 = await mem.captureEpisode('kuro', 'Standup', { start: '...', end: '...' });
const results = await mem.searchEpisode(ep.id, 'database migration');
const { summary } = await mem.summarizeEpisode(ep.id);  // requires LLM
```

### Memory Compression & Consolidation
Consolidate redundant memories into digests:
```javascript
await mem.compress([id1, id2, id3], { method: 'llm', archiveOriginals: true });
await mem.compressEpisode(episodeId);
await mem.autoCompress({ minClusterSize: