-
Notifications
You must be signed in to change notification settings - Fork 0
API Reference
Memory-Spark exposes its functionality through two layers: the OpenClaw memory plugin tools (agent-facing) and the MemorySparkManager class (programmatic API). Additionally, the StorageBackend interface provides direct LanceDB operations.
These are the tools available to OpenClaw agents via the memory plugin system.
Semantically search agent memory files (MEMORY.md, memory/*.md, and optional session transcripts).
// Tool signature
{
name: "memory_search",
parameters: {
query: string; // Required. Search query text.
maxResults?: number; // Optional. Max results to return (default: 10)
minScore?: number; // Optional. Minimum relevance score [0, 1] (default: 0.75)
}
}
// Return type
{
results: Array<{
path: string; // Relative file path (e.g., "memory/2026-01-15.md")
startLine: number; // Start line in source file
endLine: number; // End line in source file
score: number; // Relevance score [0, 1]
snippet: string; // Matched text content
source: "memory" | "sessions";
citation?: string; // "path:startLine" (when citations enabled)
}>;
provider: string; // e.g., "spark:nvidia/llama-embed-nemotron-8b"
model?: string;
fallback?: { from: string; reason?: string };
citations: "on" | "off" | "auto";
mode?: string; // Search mode (e.g., "search", "query")
}
// Error return
{
results: [];
disabled: true;
unavailable: true;
error: string; // Error message
warning: string; // Human-readable explanation
action: string; // Suggested fix
}Example usage:
Agent calls: memory_search({ query: "Klein's preferred coding style" })
→ Returns top snippets from MEMORY.md and memory/*.md mentioning coding preferences
Behavior notes:
- Citations are auto-included in direct chats, suppressed in group/channel chats
- Results are clamped by
maxInjectedCharsbudget (default: 4000 chars for QMD backend) - Quota exhaustion returns
unavailable: truewith actionable fix suggestion
Read a specific file or line range from agent memory. Use after memory_search to pull only needed lines.
// Tool signature
{
name: "memory_get",
parameters: {
path: string; // Required. Relative path (e.g., "MEMORY.md", "memory/2026-01-15.md")
from?: number; // Optional. Start line number (1-indexed)
lines?: number; // Optional. Number of lines to read (default: 50)
}
}
// Return type
{
text: string; // File content (or line range)
path: string; // Resolved path
}
// Error return
{
path: string;
text: "";
disabled: true;
error: string;
}Example usage:
memory_get({ path: "memory/2026-03-15.md", from: 10, lines: 20 })
→ Returns lines 10-30 of that daily memory file
The before_prompt_build hook automatically injects relevant memories before each agent response. This is NOT a tool — it runs transparently.
// Hook interface
interface AutoRecallDeps {
cfg: AutoRecallConfig;
backend: StorageBackend;
embed: EmbedProvider | EmbedQueue | EmbedLike;
reranker: Reranker;
hyde?: HydeConfig;
}
// Hook output (injected into agent system prompt)
type BeforePromptBuildResult = {
systemPrompt?: string; // Not used by recall
prependContext?: string; // XML-wrapped memories prepended to context
};Format of injected memories:
<relevant-memories>
<memory source="memory-spark:memory:MEMORY.md" score="0.92" updated="2026-03-15T10:30:00Z">
Klein prefers concise, direct responses without excessive formatting.
</memory>
<memory source="memory-spark:capture:auto" score="0.87" updated="2026-04-01T14:22:00Z">
DGX Spark runs llama-embed-nemotron-8b for embeddings on port 18091.
</memory>
</relevant-memories>The after_message hook captures knowledge from assistant responses.
// Configuration
interface AutoCaptureConfig {
enabled: boolean;
agents: string[]; // ["*"] for all agents
ignoreAgents: string[]; // Exclude specific agents
categories: string[]; // ["fact", "preference", "decision", "code-snippet"]
minConfidence: number; // 0.6 — minimum classification confidence
minMessageLength: number; // 30 — skip trivial messages
useClassifier: boolean; // true — use Spark zero-shot classifier
}The MemorySparkManager class (src/manager.ts) implements the MemorySearchManager interface.
interface ManagerOptions {
cfg: MemorySparkConfig;
agentId: string;
workspaceDir: string;
backend: StorageBackend;
embed: EmbedProvider;
reranker: Reranker;
queue?: EmbedQueue;
}
const manager = new MemorySparkManager(opts);Full pipeline search (hybrid merge → source weighting → temporal decay → MMR → rerank).
async search(
query: string,
opts?: { maxResults?: number; minScore?: number; sessionKey?: string }
): Promise<MemorySearchResult[]>Graceful degradation: If Spark embedding is down, vector search is disabled and only FTS results are returned.
Read file content — tries indexed chunks first, falls back to disk read.
async readFile(params: {
relPath: string;
from?: number;
lines?: number;
}): Promise<{ text: string; path: string }>Returns backend status including provider, model, dimensions, and available sources.
status(): MemoryProviderStatus
// Returns: { backend: "builtin", provider: "spark:...", model: "...", ... }Checks if the embedding provider is reachable.
async probeEmbeddingAvailability(): Promise<{ ok: boolean; error?: string }>Checks if the vector index is ready.
async probeVectorAvailability(): Promise<boolean>Closes the storage backend connection.
async close(): Promise<void>The StorageBackend interface (src/storage/backend.ts) defines the contract for storage implementations.
ANN search using IVF_PQ index with cosine distance.
async vectorSearch(queryVector: number[], opts: SearchOptions): Promise<SearchResult[]>
interface SearchOptions {
query: string;
maxResults?: number; // Default: 20
minScore?: number; // Default: 0 (no filter)
agentId?: string; // Filter by agent
source?: string; // Filter by source type
contentType?: string; // Filter by content_type
pathContains?: string; // Substring match on path
pool?: string; // Single pool filter
pools?: string[]; // Multi-pool filter (OR logic)
}BM25 full-text search with sigmoid score normalization.
async ftsSearch(query: string, opts: SearchOptions): Promise<SearchResult[]>Insert or update chunks (merge by ID). Uses write mutex to prevent concurrent commit conflicts.
async upsert(chunks: MemoryChunk[]): Promise<void>
// Retries up to 3 times on commit conflict with exponential backoffDelete all chunks for a file path.
async deleteByPath(path: string, agentId?: string): Promise<number> // returns deleted countRetrieve chunks by ID — used for parent-child context expansion.
async getByIds(ids: string[]): Promise<MemoryChunk[]>
// Arrow Vector objects are converted to plain JS number[] for cosine computationList all indexed file paths with chunk counts.
async listPaths(agentId?: string): Promise<Array<{
path: string;
agentId: string;
updatedAt: string;
chunkCount: number;
}>>Backend health check.
async status(): Promise<BackendStatus>
interface BackendStatus {
backend: "lancedb";
chunkCount: number;
tableExists: boolean;
ready: boolean;
error?: string;
}Factory function that creates a Spark reranker or passthrough fallback.
async function createReranker(cfg: RerankConfig): Promise<Reranker>
interface Reranker {
rerank(query: string, candidates: SearchResult[], topN?: number, options?: RerankOptions): Promise<SearchResult[]>;
probe(): Promise<boolean>;
}interface RerankOptions {
alphaOverride?: number; // Override blend alpha (0 = pure reranker)
blendModeOverride?: "score" | "rrf"; // Override blend mode
rrfKOverride?: number; // Override RRF k constant
vectorWeightOverride?: number; // Override vector weight for RRF
rerankerWeightOverride?: number; // Override reranker weight for RRF
gateOverride?: "off" | "hard" | "soft"; // Override gate mode
gateThresholdOverride?: number; // Override gate threshold
gateLowThresholdOverride?: number; // Override gate low threshold
}interface EmbedProvider {
id: string; // Provider identifier
model: string; // Model name
dims: number; // Vector dimensions (4096 for Nemotron-8B)
embedQuery(text: string): Promise<number[]>; // With instruction prefix
embedDocument(text: string): Promise<number[]>; // Without prefix
probe(): Promise<boolean>; // Health check
}Batched, queued embedding with automatic retry.
class EmbedQueue {
constructor(provider: EmbedProvider);
embedQuery(text: string): Promise<number[]>;
embedDocument(text: string): Promise<number[]>;
}| Error | Cause | Resolution |
|---|---|---|
insufficient_quota |
Embedding provider quota exhausted | Top up quota or switch provider |
memory search unavailable |
Provider unreachable or misconfigured | Check Spark endpoint and API key |
Commit conflict |
Concurrent LanceDB writes | Automatic retry (up to 3x) |
FTS index creation failure |
SQLite FTS5 not available | Non-fatal; FTS search returns empty |
Arrow schema error on mergeInsert |
Old table schema missing new columns | Run scripts/rebuild-table.ts
|
SCOPE_MISMATCH |
Agent trying to access unauthorized pool | Check agent scope configuration |
| Cache | Scope | Size | TTL | Eviction |
|---|---|---|---|---|
| Query embed cache | Per-process | 256 entries | 30 min | LRU |
| QMD manager cache | Per-process (global singleton) | Per agent+config | Session lifetime | On manager close |
| FTS index state | Per-backend | Boolean flag | Permanent | Reset on new table |
| Write mutex | Per-backend | 1 lock | N/A | Released on completion |