remembra 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Remembra TypeScript SDK Types
3
+ */
4
+ interface RemembraConfig {
5
+ /** Remembra server URL */
6
+ url?: string;
7
+ /** API key for authentication */
8
+ apiKey?: string;
9
+ /** User ID for memory operations */
10
+ userId: string;
11
+ /** Project namespace */
12
+ project?: string;
13
+ /** Request timeout in milliseconds */
14
+ timeout?: number;
15
+ /** Enable debug logging */
16
+ debug?: boolean;
17
+ }
18
+ interface EntityRef {
19
+ id: string;
20
+ canonical_name: string;
21
+ type: string;
22
+ confidence: number;
23
+ }
24
+ interface Memory {
25
+ id: string;
26
+ content: string;
27
+ relevance: number;
28
+ created_at: string;
29
+ metadata?: Record<string, unknown>;
30
+ }
31
+ interface StoreOptions {
32
+ /** Optional key-value metadata */
33
+ metadata?: Record<string, unknown>;
34
+ /** Time-to-live (e.g., "30d", "1y") */
35
+ ttl?: string;
36
+ }
37
+ interface StoreResult {
38
+ id: string;
39
+ extracted_facts: string[];
40
+ entities: EntityRef[];
41
+ }
42
+ interface RecallOptions {
43
+ /** Maximum results (1-50) */
44
+ limit?: number;
45
+ /** Minimum relevance threshold (0.0-1.0) */
46
+ threshold?: number;
47
+ /** Maximum tokens in context */
48
+ maxTokens?: number;
49
+ /** Enable hybrid search */
50
+ enableHybrid?: boolean;
51
+ /** Enable reranking */
52
+ enableRerank?: boolean;
53
+ }
54
+ interface RecallResult {
55
+ context: string;
56
+ memories: Memory[];
57
+ entities: EntityRef[];
58
+ }
59
+ interface ForgetOptions {
60
+ /** Delete specific memory by ID */
61
+ memoryId?: string;
62
+ /** Delete all memories about an entity */
63
+ entity?: string;
64
+ }
65
+ interface ForgetResult {
66
+ deleted_memories: number;
67
+ deleted_entities: number;
68
+ deleted_relationships: number;
69
+ }
70
+ interface Message {
71
+ /** Message role: user, assistant, or system */
72
+ role: 'user' | 'assistant' | 'system';
73
+ /** Message content */
74
+ content: string;
75
+ /** Optional speaker name */
76
+ name?: string;
77
+ /** Optional timestamp (ISO format) */
78
+ timestamp?: string;
79
+ /** Optional metadata */
80
+ metadata?: Record<string, unknown>;
81
+ }
82
+ interface IngestOptions {
83
+ /** Session ID for grouping conversations */
84
+ sessionId?: string;
85
+ /** Which messages to extract from */
86
+ extractFrom?: 'user' | 'assistant' | 'both';
87
+ /** Minimum importance threshold (0.0-1.0) */
88
+ minImportance?: number;
89
+ /** Enable deduplication */
90
+ dedupe?: boolean;
91
+ /** Store results (false for dry-run) */
92
+ store?: boolean;
93
+ /** Enable extraction (false to store raw) */
94
+ infer?: boolean;
95
+ }
96
+ interface ExtractedFact {
97
+ content: string;
98
+ confidence: number;
99
+ importance: number;
100
+ source_message_index: number;
101
+ speaker: string | null;
102
+ stored: boolean;
103
+ memory_id: string | null;
104
+ action: 'add' | 'update' | 'delete' | 'noop' | 'skipped';
105
+ action_reason: string | null;
106
+ }
107
+ interface ExtractedEntity {
108
+ name: string;
109
+ type: string;
110
+ relationship: string | null;
111
+ }
112
+ interface IngestStats {
113
+ messages_processed: number;
114
+ facts_extracted: number;
115
+ facts_stored: number;
116
+ facts_updated: number;
117
+ facts_deduped: number;
118
+ facts_skipped: number;
119
+ entities_found: number;
120
+ processing_time_ms: number;
121
+ }
122
+ interface IngestResult {
123
+ status: 'ok' | 'partial' | 'error';
124
+ session_id: string | null;
125
+ facts: ExtractedFact[];
126
+ entities: ExtractedEntity[];
127
+ stats: IngestStats;
128
+ }
129
+
130
+ /**
131
+ * Remembra TypeScript Client
132
+ *
133
+ * Main interface for the Remembra AI Memory Layer.
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * import { Remembra } from 'remembra';
138
+ *
139
+ * const memory = new Remembra({
140
+ * url: 'http://localhost:8787',
141
+ * apiKey: 'your-api-key',
142
+ * userId: 'user_123',
143
+ * });
144
+ *
145
+ * // Store a memory
146
+ * const stored = await memory.store('User prefers dark mode');
147
+ *
148
+ * // Recall memories
149
+ * const result = await memory.recall('What are user preferences?');
150
+ * console.log(result.context);
151
+ *
152
+ * // Ingest a conversation
153
+ * const ingestResult = await memory.ingestConversation([
154
+ * { role: 'user', content: 'My name is John' },
155
+ * { role: 'assistant', content: 'Nice to meet you, John!' },
156
+ * ]);
157
+ * ```
158
+ */
159
+
160
+ declare class Remembra {
161
+ private readonly url;
162
+ private readonly apiKey?;
163
+ private readonly userId;
164
+ private readonly project;
165
+ private readonly timeout;
166
+ private readonly debug;
167
+ constructor(config: RemembraConfig);
168
+ private log;
169
+ private request;
170
+ private sleep;
171
+ /**
172
+ * Store a new memory.
173
+ *
174
+ * @param content - Text content to memorize
175
+ * @param options - Optional metadata and TTL
176
+ * @returns Stored memory with extracted facts and entities
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * const result = await memory.store('User prefers dark mode', {
181
+ * metadata: { source: 'settings' },
182
+ * ttl: '30d',
183
+ * });
184
+ * console.log(result.extracted_facts);
185
+ * ```
186
+ */
187
+ store(content: string, options?: StoreOptions): Promise<StoreResult>;
188
+ /**
189
+ * Recall memories relevant to a query.
190
+ *
191
+ * @param query - Natural language query
192
+ * @param options - Recall options (limit, threshold, etc.)
193
+ * @returns Context string and matching memories
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * const result = await memory.recall('What are user preferences?');
198
+ * console.log(result.context); // Synthesized context
199
+ * console.log(result.memories); // Individual memories
200
+ * ```
201
+ */
202
+ recall(query: string, options?: RecallOptions): Promise<RecallResult>;
203
+ /**
204
+ * Get a specific memory by ID.
205
+ *
206
+ * @param memoryId - Memory ID
207
+ * @returns Memory details
208
+ */
209
+ get(memoryId: string): Promise<Memory>;
210
+ /**
211
+ * Forget (delete) memories.
212
+ *
213
+ * @param options - What to delete (memoryId, entity, or all)
214
+ * @returns Deletion counts
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * // Delete specific memory
219
+ * await memory.forget({ memoryId: 'mem_123' });
220
+ *
221
+ * // Delete all about an entity
222
+ * await memory.forget({ entity: 'John' });
223
+ * ```
224
+ */
225
+ forget(options?: ForgetOptions): Promise<ForgetResult>;
226
+ /**
227
+ * Ingest a conversation and automatically extract memories.
228
+ *
229
+ * This is the primary method for AI agents to add conversation context
230
+ * to persistent memory without manually calling store for each fact.
231
+ *
232
+ * @param messages - Array of conversation messages
233
+ * @param options - Ingestion options
234
+ * @returns Extracted facts, entities, and stats
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * const result = await memory.ingestConversation([
239
+ * { role: 'user', content: 'My name is John and I work at Google' },
240
+ * { role: 'assistant', content: 'Nice to meet you, John!' },
241
+ * ], {
242
+ * minImportance: 0.5,
243
+ * });
244
+ *
245
+ * console.log(`Extracted ${result.stats.facts_extracted} facts`);
246
+ * console.log(`Stored ${result.stats.facts_stored} new memories`);
247
+ * ```
248
+ */
249
+ ingestConversation(messages: Message[], options?: IngestOptions): Promise<IngestResult>;
250
+ /**
251
+ * Check server health.
252
+ *
253
+ * @returns Health status
254
+ */
255
+ health(): Promise<Record<string, unknown>>;
256
+ /**
257
+ * List entities for the current user.
258
+ *
259
+ * @returns Array of entities
260
+ */
261
+ listEntities(): Promise<EntityRef[]>;
262
+ }
263
+
264
+ /**
265
+ * Remembra SDK Error Classes
266
+ */
267
+ declare class RemembraError extends Error {
268
+ readonly status?: number;
269
+ readonly code?: string;
270
+ constructor(message: string, status?: number, code?: string);
271
+ }
272
+ declare class AuthenticationError extends RemembraError {
273
+ constructor(message?: string);
274
+ }
275
+ declare class NotFoundError extends RemembraError {
276
+ constructor(message?: string);
277
+ }
278
+ declare class ValidationError extends RemembraError {
279
+ constructor(message: string);
280
+ }
281
+ declare class RateLimitError extends RemembraError {
282
+ readonly retryAfter?: number;
283
+ constructor(message?: string, retryAfter?: number);
284
+ }
285
+ declare class ServerError extends RemembraError {
286
+ constructor(message?: string);
287
+ }
288
+ declare class NetworkError extends RemembraError {
289
+ constructor(message?: string);
290
+ }
291
+ declare class TimeoutError extends RemembraError {
292
+ constructor(message?: string);
293
+ }
294
+
295
+ export { AuthenticationError, type EntityRef, type ExtractedEntity, type ExtractedFact, type ForgetOptions, type ForgetResult, type IngestOptions, type IngestResult, type IngestStats, type Memory, type Message, NetworkError, NotFoundError, RateLimitError, type RecallOptions, type RecallResult, Remembra, type RemembraConfig, RemembraError, ServerError, type StoreOptions, type StoreResult, TimeoutError, ValidationError };
package/dist/index.js ADDED
@@ -0,0 +1,375 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AuthenticationError: () => AuthenticationError,
24
+ NetworkError: () => NetworkError,
25
+ NotFoundError: () => NotFoundError,
26
+ RateLimitError: () => RateLimitError,
27
+ Remembra: () => Remembra,
28
+ RemembraError: () => RemembraError,
29
+ ServerError: () => ServerError,
30
+ TimeoutError: () => TimeoutError,
31
+ ValidationError: () => ValidationError
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/errors.ts
36
+ var RemembraError = class _RemembraError extends Error {
37
+ constructor(message, status, code) {
38
+ super(message);
39
+ this.name = "RemembraError";
40
+ this.status = status;
41
+ this.code = code;
42
+ if (Error.captureStackTrace) {
43
+ Error.captureStackTrace(this, _RemembraError);
44
+ }
45
+ }
46
+ };
47
+ var AuthenticationError = class extends RemembraError {
48
+ constructor(message = "Authentication failed") {
49
+ super(message, 401, "AUTH_ERROR");
50
+ this.name = "AuthenticationError";
51
+ }
52
+ };
53
+ var NotFoundError = class extends RemembraError {
54
+ constructor(message = "Resource not found") {
55
+ super(message, 404, "NOT_FOUND");
56
+ this.name = "NotFoundError";
57
+ }
58
+ };
59
+ var ValidationError = class extends RemembraError {
60
+ constructor(message) {
61
+ super(message, 422, "VALIDATION_ERROR");
62
+ this.name = "ValidationError";
63
+ }
64
+ };
65
+ var RateLimitError = class extends RemembraError {
66
+ constructor(message = "Rate limit exceeded", retryAfter) {
67
+ super(message, 429, "RATE_LIMITED");
68
+ this.name = "RateLimitError";
69
+ this.retryAfter = retryAfter;
70
+ }
71
+ };
72
+ var ServerError = class extends RemembraError {
73
+ constructor(message = "Internal server error") {
74
+ super(message, 500, "SERVER_ERROR");
75
+ this.name = "ServerError";
76
+ }
77
+ };
78
+ var NetworkError = class extends RemembraError {
79
+ constructor(message = "Network error") {
80
+ super(message, void 0, "NETWORK_ERROR");
81
+ this.name = "NetworkError";
82
+ }
83
+ };
84
+ var TimeoutError = class extends RemembraError {
85
+ constructor(message = "Request timed out") {
86
+ super(message, void 0, "TIMEOUT");
87
+ this.name = "TimeoutError";
88
+ }
89
+ };
90
+
91
+ // src/client.ts
92
+ var DEFAULT_URL = "http://localhost:8787";
93
+ var DEFAULT_TIMEOUT = 3e4;
94
+ var MAX_RETRIES = 3;
95
+ var RETRY_DELAY = 1e3;
96
+ var Remembra = class {
97
+ constructor(config) {
98
+ this.url = (config.url || DEFAULT_URL).replace(/\/$/, "");
99
+ this.apiKey = config.apiKey;
100
+ this.userId = config.userId;
101
+ this.project = config.project || "default";
102
+ this.timeout = config.timeout || DEFAULT_TIMEOUT;
103
+ this.debug = config.debug || false;
104
+ if (!this.userId) {
105
+ throw new ValidationError("userId is required");
106
+ }
107
+ }
108
+ // ===========================================================================
109
+ // Private Methods
110
+ // ===========================================================================
111
+ log(...args) {
112
+ if (this.debug) {
113
+ console.log("[Remembra]", ...args);
114
+ }
115
+ }
116
+ async request(method, path, options = {}) {
117
+ const { body, params, retries = MAX_RETRIES } = options;
118
+ const url = new URL(`${this.url}${path}`);
119
+ if (params) {
120
+ Object.entries(params).forEach(([key, value]) => {
121
+ if (value !== void 0) {
122
+ url.searchParams.set(key, value);
123
+ }
124
+ });
125
+ }
126
+ const headers = {
127
+ "Content-Type": "application/json",
128
+ "User-Agent": "remembra-js/0.1.0"
129
+ };
130
+ if (this.apiKey) {
131
+ headers["X-API-Key"] = this.apiKey;
132
+ }
133
+ this.log(method, path, body ? JSON.stringify(body).slice(0, 100) : "");
134
+ let lastError;
135
+ for (let attempt = 0; attempt < retries; attempt++) {
136
+ try {
137
+ const controller = new AbortController();
138
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
139
+ const response = await fetch(url.toString(), {
140
+ method,
141
+ headers,
142
+ body: body ? JSON.stringify(body) : void 0,
143
+ signal: controller.signal
144
+ });
145
+ clearTimeout(timeoutId);
146
+ if (response.ok) {
147
+ return await response.json();
148
+ }
149
+ const errorBody = await response.json().catch(() => ({}));
150
+ const errorMessage = errorBody.detail || response.statusText;
151
+ switch (response.status) {
152
+ case 401:
153
+ throw new AuthenticationError(errorMessage);
154
+ case 404:
155
+ throw new NotFoundError(errorMessage);
156
+ case 422:
157
+ throw new ValidationError(errorMessage);
158
+ case 429:
159
+ const retryAfter = parseInt(response.headers.get("Retry-After") || "60", 10);
160
+ if (attempt < retries - 1) {
161
+ this.log(`Rate limited, retrying in ${retryAfter}s...`);
162
+ await this.sleep(retryAfter * 1e3);
163
+ continue;
164
+ }
165
+ throw new RateLimitError(errorMessage, retryAfter);
166
+ case 500:
167
+ case 502:
168
+ case 503:
169
+ case 504:
170
+ if (attempt < retries - 1) {
171
+ this.log(`Server error (${response.status}), retrying...`);
172
+ await this.sleep(RETRY_DELAY * Math.pow(2, attempt));
173
+ continue;
174
+ }
175
+ throw new ServerError(errorMessage);
176
+ default:
177
+ throw new RemembraError(errorMessage, response.status);
178
+ }
179
+ } catch (error) {
180
+ lastError = error;
181
+ if (error instanceof RemembraError) {
182
+ throw error;
183
+ }
184
+ if (error instanceof Error) {
185
+ if (error.name === "AbortError") {
186
+ throw new TimeoutError(`Request timed out after ${this.timeout}ms`);
187
+ }
188
+ if (attempt < retries - 1) {
189
+ this.log(`Network error, retrying...`, error.message);
190
+ await this.sleep(RETRY_DELAY * Math.pow(2, attempt));
191
+ continue;
192
+ }
193
+ throw new NetworkError(error.message);
194
+ }
195
+ }
196
+ }
197
+ throw lastError || new NetworkError("Request failed");
198
+ }
199
+ sleep(ms) {
200
+ return new Promise((resolve) => setTimeout(resolve, ms));
201
+ }
202
+ // ===========================================================================
203
+ // Core Operations
204
+ // ===========================================================================
205
+ /**
206
+ * Store a new memory.
207
+ *
208
+ * @param content - Text content to memorize
209
+ * @param options - Optional metadata and TTL
210
+ * @returns Stored memory with extracted facts and entities
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * const result = await memory.store('User prefers dark mode', {
215
+ * metadata: { source: 'settings' },
216
+ * ttl: '30d',
217
+ * });
218
+ * console.log(result.extracted_facts);
219
+ * ```
220
+ */
221
+ async store(content, options = {}) {
222
+ return this.request("POST", "/api/v1/memories", {
223
+ body: {
224
+ user_id: this.userId,
225
+ project_id: this.project,
226
+ content,
227
+ metadata: options.metadata || {},
228
+ ttl: options.ttl
229
+ }
230
+ });
231
+ }
232
+ /**
233
+ * Recall memories relevant to a query.
234
+ *
235
+ * @param query - Natural language query
236
+ * @param options - Recall options (limit, threshold, etc.)
237
+ * @returns Context string and matching memories
238
+ *
239
+ * @example
240
+ * ```typescript
241
+ * const result = await memory.recall('What are user preferences?');
242
+ * console.log(result.context); // Synthesized context
243
+ * console.log(result.memories); // Individual memories
244
+ * ```
245
+ */
246
+ async recall(query, options = {}) {
247
+ return this.request("POST", "/api/v1/memories/recall", {
248
+ body: {
249
+ user_id: this.userId,
250
+ project_id: this.project,
251
+ query,
252
+ limit: options.limit || 5,
253
+ threshold: options.threshold || 0.4,
254
+ max_tokens: options.maxTokens,
255
+ enable_hybrid: options.enableHybrid,
256
+ enable_rerank: options.enableRerank
257
+ }
258
+ });
259
+ }
260
+ /**
261
+ * Get a specific memory by ID.
262
+ *
263
+ * @param memoryId - Memory ID
264
+ * @returns Memory details
265
+ */
266
+ async get(memoryId) {
267
+ return this.request("GET", `/api/v1/memories/${memoryId}`);
268
+ }
269
+ /**
270
+ * Forget (delete) memories.
271
+ *
272
+ * @param options - What to delete (memoryId, entity, or all)
273
+ * @returns Deletion counts
274
+ *
275
+ * @example
276
+ * ```typescript
277
+ * // Delete specific memory
278
+ * await memory.forget({ memoryId: 'mem_123' });
279
+ *
280
+ * // Delete all about an entity
281
+ * await memory.forget({ entity: 'John' });
282
+ * ```
283
+ */
284
+ async forget(options = {}) {
285
+ const params = {};
286
+ if (options.memoryId) {
287
+ params.memory_id = options.memoryId;
288
+ } else if (options.entity) {
289
+ params.entity = options.entity;
290
+ } else {
291
+ params.user_id = this.userId;
292
+ }
293
+ return this.request("DELETE", "/api/v1/memories", { params });
294
+ }
295
+ // ===========================================================================
296
+ // Conversation Ingestion
297
+ // ===========================================================================
298
+ /**
299
+ * Ingest a conversation and automatically extract memories.
300
+ *
301
+ * This is the primary method for AI agents to add conversation context
302
+ * to persistent memory without manually calling store for each fact.
303
+ *
304
+ * @param messages - Array of conversation messages
305
+ * @param options - Ingestion options
306
+ * @returns Extracted facts, entities, and stats
307
+ *
308
+ * @example
309
+ * ```typescript
310
+ * const result = await memory.ingestConversation([
311
+ * { role: 'user', content: 'My name is John and I work at Google' },
312
+ * { role: 'assistant', content: 'Nice to meet you, John!' },
313
+ * ], {
314
+ * minImportance: 0.5,
315
+ * });
316
+ *
317
+ * console.log(`Extracted ${result.stats.facts_extracted} facts`);
318
+ * console.log(`Stored ${result.stats.facts_stored} new memories`);
319
+ * ```
320
+ */
321
+ async ingestConversation(messages, options = {}) {
322
+ return this.request("POST", "/api/v1/ingest/conversation", {
323
+ body: {
324
+ messages,
325
+ user_id: this.userId,
326
+ project_id: this.project,
327
+ session_id: options.sessionId,
328
+ options: {
329
+ extract_from: options.extractFrom || "both",
330
+ min_importance: options.minImportance ?? 0.5,
331
+ dedupe: options.dedupe ?? true,
332
+ store: options.store ?? true,
333
+ infer: options.infer ?? true
334
+ }
335
+ }
336
+ });
337
+ }
338
+ // ===========================================================================
339
+ // Utilities
340
+ // ===========================================================================
341
+ /**
342
+ * Check server health.
343
+ *
344
+ * @returns Health status
345
+ */
346
+ async health() {
347
+ return this.request("GET", "/health");
348
+ }
349
+ /**
350
+ * List entities for the current user.
351
+ *
352
+ * @returns Array of entities
353
+ */
354
+ async listEntities() {
355
+ const result = await this.request(
356
+ "GET",
357
+ "/api/v1/entities",
358
+ { params: { user_id: this.userId, project_id: this.project } }
359
+ );
360
+ return result.entities || [];
361
+ }
362
+ };
363
+ // Annotate the CommonJS export names for ESM import in node:
364
+ 0 && (module.exports = {
365
+ AuthenticationError,
366
+ NetworkError,
367
+ NotFoundError,
368
+ RateLimitError,
369
+ Remembra,
370
+ RemembraError,
371
+ ServerError,
372
+ TimeoutError,
373
+ ValidationError
374
+ });
375
+ //# sourceMappingURL=index.js.map