@dn-inc/openclaw-seahorse
v0.2.2
Published
Seahorse memory plugin for OpenClaw
Readme
Seahorse Memory Plugin for OpenClaw
Drop-in replacement for OpenClaw's built-in memory search, powered by Seahorse.
- Uploads local memory files (
MEMORY.md,memory/**/*.md) to Seahorse Storage - Syncs the entire workspace for full semantic search (
syncWorkspace: true) - Routes
memory_searchto Seahorse's vector search API - Keeps
memory_getreading from local files (same as native)
Why Seahorse?
OpenClaw's built-in memory only indexes markdown notes the agent explicitly writes (MEMORY.md and memory/**/*.md). But agents produce far more than markdown — JSON data, YAML configs, PDF reports, images, spreadsheets, and other artifacts accumulate in the workspace over time, invisible to memory search.
With syncWorkspace: true, Seahorse indexes every supported file in the workspace automatically. The agent can semantically search across all of its own output — not just what it remembered to write down. Documents, data files, images, and markup (37 supported extensions) all become searchable through the same memory_search tool, with no API or workflow changes.
Requirements
- Node.js 20+
- OpenClaw >= 2026.1.26
- Seahorse account (Storage + Table tenants)
Quick Start
- Install the plugin:
openclaw plugins install @dn-inc/openclaw-seahorse- Get your Storage URL, Table URL, and API key from the Seahorse console, then add to
plugins.entriesinopenclaw.json:
"openclaw-seahorse": {
"config": {
"storageUrl": "https://<your-storage-id>.api.seahorse.dnotitia.ai",
"tableUrl": "https://<your-table-id>.api.seahorse.dnotitia.ai",
"apiKey": "your-key-here",
// Optional: search your entire workspace, not just memory files
"syncWorkspace": true
}
}The apiKey field supports ${ENV_VAR} syntax (e.g., "${SEAHORSE_API_KEY}"), or you can omit it entirely and set the SEAHORSE_API_KEY environment variable.
That's it. The plugin auto-syncs your files on startup and watches for changes in real time.
File Sync
The plugin runs a background service (seahorse-sync) that keeps Seahorse Storage in sync:
- Initial sync: scans files on startup, uploads only changed files
- Live watch: detects file create/modify/delete via
fs.watch(1.5s debounce) - Dedup: mtime-based state tracking prevents redundant uploads
- Delete sync: local file deletion triggers Seahorse Storage deletion
By default, only memory files (MEMORY.md + memory/**/*.md) are synced. Set syncWorkspace: true to sync the entire workspace — all supported files become searchable via memory_search without any tool or API changes.
Supported file extensions (workspace sync):
| Category | Extensions |
|----------|-----------|
| Documents | .doc, .docx, .hwp, .hwpx, .odg, .odp, .odt, .pdf, .ppt, .pptx, .rtf |
| Data | .csv, .json, .ods, .toml, .tsv, .xls, .xlsx, .yaml, .yml |
| Images | .bmp, .gif, .jpeg, .jpg, .png, .tif, .tiff, .webp |
| Text / Markup | .epub, .htm, .html, .markdown, .md, .text, .txt, .xhtml, .xml |
When workspace sync is enabled, dotfiles/dotdirs (.*) are always excluded. Add further patterns via syncBlacklist:
{
"storageUrl": "...", "tableUrl": "...", "apiKey": "...",
"syncWorkspace": true,
"syncBlacklist": ["node_modules/", "dist/", "*.log", "*.tmp"]
}Supported blacklist patterns:
| Pattern | Example | Matches |
|---------|---------|---------|
| "dir/" | "node_modules/" | Directory with that exact name |
| "*.ext" | "*.tmp" | Any segment ending with .tmp |
| "prefix*" | ".*" | Any segment starting with . (built-in) |
| "name" | "Thumbs.db" | Exact filename in any directory |
Storage Layout
All synced files are stored under the openclaw/ prefix in Seahorse Storage (e.g., MEMORY.md → openclaw/MEMORY.md). This isolates plugin data from other data in the same Seahorse tenant. Search results are mapped back to workspace-relative paths automatically.
Tools
memory_search
Semantic vector search over synced files via Seahorse.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| query | string | Yes | Search query |
| maxResults | number | No | Max results (default: topK config) |
| minScore | number | No | Min relevance score 0-1 (default: minScore config) |
memory_get
Read a local file by path.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| path | string | Yes | Relative path (e.g., MEMORY.md, memory/notes.md) |
| from | number | No | Start line (1-based) |
| lines | number | No | Number of lines to read |
By default, allowed paths are MEMORY.md and files under memory/ only. With syncWorkspace: true, any workspace file is accessible (except blacklisted paths).
Config Reference
Layer 0: Connection (required)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| storageUrl | string | Yes | Seahorse Storage tenant URL |
| tableUrl | string | Yes | Seahorse Table tenant URL |
| apiKey | string | No | API key. Auto-reads from SEAHORSE_API_KEY env var if omitted. Supports ${ENV_VAR} syntax. |
Layer 1: Search & Sync Behavior (optional)
These override the OpenClaw-compatible defaults.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| topK | number | 6 | Number of results to return |
| minScore | number | 0.35 | Minimum relevance score (0-1) |
| searchMode | string | "hybrid" | "dense", "sparse", or "hybrid" |
| syncWorkspace | boolean | false | Sync entire workspace (not just memory files) |
| syncBlacklist | string[] | [] | Additional blacklist patterns for workspace sync (see File Sync) |
Example — return more results with a lower score threshold:
{
"storageUrl": "...", "tableUrl": "...", "apiKey": "...",
"topK": 20,
"minScore": 0.2
}Layer 2: Seahorse Tuning (optional, advanced)
Fine-grained control over Seahorse search parameters. Omitted values use Seahorse server defaults.
dense — Dense Vector Search
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| dense.column | string | "dense_vector" | Dense vector column name |
| dense.efSearch | number | (server) | HNSW ef_search parameter |
sparse — Sparse (BM25) Search
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| sparse.column | string | "sparse_vector" | Sparse vector column name |
| sparse.b | number | (server: 0.75) | BM25 document length normalization (0-1) |
| sparse.k | number | (server: 1.2) | BM25 term frequency saturation |
fusion — Hybrid Fusion
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| fusion.type | string | "rrf" | Fusion algorithm |
| fusion.alpha | number | 0.7 | Dense weight (0-1). Sparse weight = 1 - alpha |
| fusion.k | number | (server: 60) | RRF k parameter |
projection
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| projection | string | "id, text, metadata, distance, score" | SQL projection for returned fields |
Example — dense-only search with custom ef_search:
{
"storageUrl": "...", "tableUrl": "...", "apiKey": "...",
"searchMode": "dense",
"dense": { "efSearch": 200 }
}Example — hybrid with equal dense/sparse weighting (Seahorse native behavior):
{
"storageUrl": "...", "tableUrl": "...", "apiKey": "...",
"fusion": { "alpha": 0.5 }
}Defaults Comparison: OpenClaw vs Seahorse
The plugin ships with OpenClaw-compatible defaults. The table below shows where these differ from Seahorse server defaults, so you can tune toward Seahorse-native behavior when desired.
| Parameter | Plugin Default | OpenClaw Native | Seahorse Server | Notes |
|-----------|---------------|-----------------|-----------------|-------|
| Search mode | hybrid | hybrid | (must specify) | OpenClaw uses hybrid by default |
| Top K | 6 | 6 | (must specify) | OpenClaw returns 6 results |
| Min score | 0.35 | 0.35 | (no server filter) | Client-side filtering |
| Fusion alpha | 0.7 | vector 70% | 0.5 | Key difference. OpenClaw weights dense at 70%. Seahorse defaults to 50/50. Set fusion.alpha: 0.5 for Seahorse-native. |
| Fusion k | (server) | N/A | 60 | Omitted; server default used |
| BM25 b | (server) | N/A | 0.75 | Omitted; server default used |
| BM25 k | (server) | N/A | 1.2 | Omitted; server default used |
| ef_search | (server) | N/A | (server decides) | Omitted; server default used |
| Dense column | dense_vector | N/A | N/A | Must match your Seahorse table schema |
| Sparse column | sparse_vector | N/A | N/A | Must match your Seahorse table schema |
| Projection | "id, text, metadata, distance, score" | N/A | (must specify) | Requests both distance (dense) and score (hybrid) for proper normalization |
Hybrid (RRF) Score Normalization: In hybrid mode, Seahorse returns raw RRF scores (~0.01-0.03). The plugin normalizes these to a 0-1 scale by dividing by the theoretical maximum (
numRankers / (k + 1)), making them compatible with theminScorethreshold. With default k=60 and 2 rankers, a raw score of 0.016 becomes ~0.49 after normalization.
