@claiv/memory
v0.6.4
Published
Official JavaScript/TypeScript SDK for the Claiv Memory API (V6 - Catalog Memory + Deterministic Routing)
Maintainers
Readme
@claiv/memory
Official JavaScript/TypeScript SDK for the Claiv Memory API, aligned with the current V6.3 API.
Installation
npm install @claiv/memoryQuick Start
import { ClaivClient } from '@claiv/memory';
const client = new ClaivClient({ apiKey: 'your-api-key' });
// Store a memory event
await client.ingest({
user_id: 'user-123',
conversation_id: 'session-abc', // required: stable conversation scope
type: 'message',
role: 'user',
content: 'I use React and TypeScript at work.',
});
// Recall relevant memory
const result = await client.recall({
user_id: 'user-123',
conversation_id: 'session-abc',
query: 'What stack does this user work with?',
});
// Inject the pre-synthesized narrative into your LLM
const messages = [
{ role: 'system', content: result.llm_context.text || 'No memory found.' },
{ role: 'user', content: userMessage },
];Memory Scoping
Facts and document chunks can be scoped to control visibility across recalls:
| scope | Visible when |
|---------|-------------|
| 'global' (default) | All recalls for this user |
| 'project' | Same project_id only |
| 'conversation' | Same conversation_id only |
// Global memory — visible everywhere (default)
await client.ingest({
user_id: 'user-123',
conversation_id: 'session-abc',
type: 'message',
role: 'user',
content: 'My name is Alex.',
// scope: 'global' is the default
});
// Project-scoped — only visible within this project
await client.ingest({
user_id: 'user-123',
conversation_id: 'session-abc',
project_id: 'project-xyz',
scope: 'project',
type: 'message',
role: 'user',
content: 'This project uses Next.js and Prisma.',
});
// Conversation-scoped — only visible in this session
await client.ingest({
user_id: 'user-123',
conversation_id: 'session-abc',
scope: 'conversation',
type: 'message',
role: 'user',
content: 'Actually, ignore the last instruction.',
});API Reference
new ClaivClient(options)
| Option | Type | Default | Description |
|--------------|------------|-------------------------|-----------------------------------|
| apiKey | string | required | API key (sent as Bearer token) |
| baseUrl | string | https://api.claiv.io | API base URL |
| timeout | number | 30000 | Request timeout in milliseconds |
| maxRetries | number | 2 | Retries on 429/5xx (0 to disable) |
| fetch | function | globalThis.fetch | Custom fetch implementation |
Core Methods
client.ingest(request) / client.ingestV6(request)
Store a memory event.
const { event_id, deduped } = await client.ingest({
user_id: 'user-123', // required
conversation_id: 'session-abc', // required
project_id: 'project-xyz', // provide when using project-scoped facts
scope: 'global', // optional: 'global' | 'project' | 'conversation'
type: 'message', // required: 'message' | 'tool_call' | 'app_event'
role: 'user', // optional: 'user' | 'assistant' | 'system' | 'tool'
content: 'The actual text', // required
metadata: { source: 'chat' }, // optional: arbitrary key-value pairs
event_time: '2025-01-01T00:00:00Z', // optional: ISO 8601 or Unix timestamp
idempotency_key: 'msg-001', // optional: prevents duplicate ingestion
});client.recall(request) / client.recallV6(request)
Retrieve relevant memory for a query. Automatically includes matching document chunks in llm_context.text when documents exist for the user.
Document retrieval mode is auto-detected from the query:
- Semantic (default): top-K chunks by cosine similarity, controlled by
limits.document_chunks. - Working-set: triggered by structural references (
"chapter 3","the introduction") — returns the full section plus a document summary and summaries of other sections.
const result = await client.recall({
user_id: 'user-123', // required
conversation_id: 'session-abc', // required
project_id: 'project-xyz', // optional: include project-scoped facts
document_id: 'doc_uuid', // optional: restrict to a specific document
query: 'What stack does this user prefer?', // required
reference_time: null, // optional: ISO datetime for temporal anchoring
limits: {
answer_facts: 12, // default: 12
supporting_facts: 12, // default: 12
background_facts: 30, // default: 30
document_chunks: 5, // default: 5 — max chunks in semantic mode (1–50)
},
include: {
pending_plan: true, // default: true
debug: false, // default: false
},
});
// result.llm_context.text → pre-synthesized narrative (inject as system prompt)
// result.answer_facts → Array<RecallFact> — direct answers to the query
// result.supporting_facts → Array<RecallFact> — corroborating evidence
// result.background_context → Array<RecallFact> — broader user context
// result.working_memory → WorkingMemory | null — current conversation state
// result.routing → { mode, kinds, predicates, temporal_intent }Recommended system prompt pattern:
const systemPrompt = result.llm_context.text
? `You are a helpful assistant. Here is what you remember about this user:\n\n${result.llm_context.text}`
: 'You are a helpful assistant.';client.uploadDocument(request)
Upload and index a document for RAG retrieval. Chunking and embedding happen synchronously — the document is fully indexed when the response returns.
const result = await client.uploadDocument({
user_id: 'user-123', // required
content: documentText, // required: full document text (up to 5 MB)
document_name: 'Product Manual', // required: shown as citation in llm_context
document_id: 'manual-v2', // optional: stable ID; re-uploading replaces all chunks
conversation_id: 'session-abc', // optional: scope chunks to this conversation
project_id: 'project-xyz', // optional: scope chunks to this project
scope: 'global', // optional: 'global' | 'project' | 'conversation'
chunk_size: 800, // optional: target chars per chunk (200–4000)
chunk_overlap: 100, // optional: overlap between chunks (0–500)
});
// result.document_id → use on recall to target this document
// result.chunks_ingested → number of chunks stored
// result.chunk_ids → UUIDs for each stored chunk
// The document is now ready — no polling needed.
// Subsequent recalls will automatically include matching chunks in llm_context.text.
// To target recall at this document only:
const recall = await client.recall({
user_id: 'user-123',
conversation_id: 'session-abc',
query: 'What does the manual say about installation?',
document_id: result.document_id,
});
// To delete the document later:
await client.forget({
user_id: 'user-123',
document_id: result.document_id,
});client.forget(request)
Delete memory matching a scope.
const { receipt_id, deleted_counts } = await client.forget({
user_id: 'user-123', // required
conversation_id: 'session-abc', // optional
project_id: 'project-xyz', // optional
document_id: 'manual-v2', // optional: removes all chunks for this document
from_time: '2025-01-01T00:00:00Z', // optional: lower bound
to_time: '2025-06-01T00:00:00Z', // optional: upper bound
});
// deleted_counts: { events, chunks, episodes, facts, claims, open_loops }Usage Methods
// Aggregated summary with daily breakdown ('7d' | '30d' | 'month' | 'today')
const summary = await client.getUsageSummary('30d');
// Breakdown by endpoint
const breakdown = await client.getUsageBreakdown('today');
// Current plan limits and quota
const limits = await client.getUsageLimits();Health Check
const { ok } = await client.healthCheck(); // no auth requiredError Handling
All errors extend ClaivError.
import { ClaivApiError, ClaivTimeoutError, ClaivNetworkError } from '@claiv/memory';
try {
await client.ingest({ ... });
} catch (err) {
if (err instanceof ClaivApiError) {
console.log(err.status); // HTTP status code
console.log(err.code); // 'invalid_request' | 'unauthorized' | 'quota_exceeded' | ...
console.log(err.requestId); // server request ID for support
console.log(err.details); // validation errors, quota info, etc.
} else if (err instanceof ClaivTimeoutError) {
// request timed out
} else if (err instanceof ClaivNetworkError) {
// DNS failure, connection refused, etc.
}
}Retries
The SDK automatically retries on 429 (rate limited) and 5xx (server error) responses with exponential backoff and jitter. Client errors (400, 401, 403, 404) are never retried.
const client = new ClaivClient({ apiKey: 'key', maxRetries: 0 }); // disable retries
const client = new ClaivClient({ apiKey: 'key', maxRetries: 5 }); // more retriesTypeScript
All request/response types are exported:
import type {
// Core V6 types
MemoryScope,
IngestRequest, IngestResponse,
RecallRequest, RecallResponse,
RecallFact, WorkingMemory, PendingPlan,
V6LLMContext, V6TemporalMatch,
ForgetRequest, ForgetResponse, DeletedCounts,
// Document types
DocumentUploadRequest, DocumentUploadResponse,
// Usage types
UsageSummaryResponse, UsageBreakdownResponse, UsageLimitsResponse,
} from '@claiv/memory';