@lanonasis/memory-client 2.1.0 → 2.2.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,1918 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+ var child_process = require('child_process');
5
+ var util = require('util');
6
+
7
+ /**
8
+ * Memory types supported by the service
9
+ */
10
+ const MEMORY_TYPES = ['context', 'project', 'knowledge', 'reference', 'personal', 'workflow'];
11
+ /**
12
+ * Memory status values
13
+ */
14
+ const MEMORY_STATUSES = ['active', 'archived', 'draft', 'deleted'];
15
+ /**
16
+ * Validation schemas using Zod
17
+ */
18
+ const createMemorySchema = zod.z.object({
19
+ title: zod.z.string().min(1).max(500),
20
+ content: zod.z.string().min(1).max(50000),
21
+ summary: zod.z.string().max(1000).optional(),
22
+ memory_type: zod.z.enum(MEMORY_TYPES).default('context'),
23
+ topic_id: zod.z.string().uuid().optional(),
24
+ project_ref: zod.z.string().max(100).optional(),
25
+ tags: zod.z.array(zod.z.string().min(1).max(50)).max(20).default([]),
26
+ metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
27
+ });
28
+ const updateMemorySchema = zod.z.object({
29
+ title: zod.z.string().min(1).max(500).optional(),
30
+ content: zod.z.string().min(1).max(50000).optional(),
31
+ summary: zod.z.string().max(1000).optional(),
32
+ memory_type: zod.z.enum(MEMORY_TYPES).optional(),
33
+ status: zod.z.enum(MEMORY_STATUSES).optional(),
34
+ topic_id: zod.z.string().uuid().nullable().optional(),
35
+ project_ref: zod.z.string().max(100).nullable().optional(),
36
+ tags: zod.z.array(zod.z.string().min(1).max(50)).max(20).optional(),
37
+ metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
38
+ });
39
+ const searchMemorySchema = zod.z.object({
40
+ query: zod.z.string().min(1).max(1000),
41
+ memory_types: zod.z.array(zod.z.enum(MEMORY_TYPES)).optional(),
42
+ tags: zod.z.array(zod.z.string()).optional(),
43
+ topic_id: zod.z.string().uuid().optional(),
44
+ project_ref: zod.z.string().optional(),
45
+ status: zod.z.enum(MEMORY_STATUSES).default('active'),
46
+ limit: zod.z.number().int().min(1).max(100).default(20),
47
+ threshold: zod.z.number().min(0).max(1).default(0.7)
48
+ });
49
+ const createTopicSchema = zod.z.object({
50
+ name: zod.z.string().min(1).max(100),
51
+ description: zod.z.string().max(500).optional(),
52
+ color: zod.z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
53
+ icon: zod.z.string().max(50).optional(),
54
+ parent_topic_id: zod.z.string().uuid().optional()
55
+ });
56
+ zod.z
57
+ .object({
58
+ memory_id: zod.z.string().uuid().optional(),
59
+ content: zod.z.string().min(1).optional(),
60
+ title: zod.z.string().optional(),
61
+ existing_tags: zod.z.array(zod.z.string()).optional(),
62
+ max_suggestions: zod.z.number().int().min(1).max(10).optional()
63
+ })
64
+ .refine((data) => data.memory_id || data.content, {
65
+ message: 'Either memory_id or content is required'
66
+ });
67
+ zod.z.object({
68
+ time_range_days: zod.z.number().int().min(1).max(365).optional(),
69
+ include_insights: zod.z.boolean().optional(),
70
+ response_format: zod.z.enum(['json', 'markdown']).optional()
71
+ });
72
+ zod.z.object({
73
+ include_recommendations: zod.z.boolean().optional(),
74
+ detailed_breakdown: zod.z.boolean().optional()
75
+ });
76
+ zod.z
77
+ .object({
78
+ memory_id: zod.z.string().uuid().optional(),
79
+ query: zod.z.string().min(1).optional(),
80
+ limit: zod.z.number().int().min(1).max(20).optional(),
81
+ similarity_threshold: zod.z.number().min(0).max(1).optional(),
82
+ exclude_ids: zod.z.array(zod.z.string().uuid()).optional()
83
+ })
84
+ .refine((data) => data.memory_id || data.query, {
85
+ message: 'Either memory_id or query is required'
86
+ });
87
+ zod.z.object({
88
+ similarity_threshold: zod.z.number().min(0).max(1).optional(),
89
+ include_archived: zod.z.boolean().optional(),
90
+ limit: zod.z.number().int().min(1).max(50).optional()
91
+ });
92
+ zod.z.object({
93
+ memory_ids: zod.z.array(zod.z.string().uuid()).optional(),
94
+ topic: zod.z.string().min(1).optional(),
95
+ time_range_days: zod.z.number().int().min(1).max(365).optional(),
96
+ insight_types: zod.z.array(zod.z.enum(['themes', 'connections', 'gaps', 'actions', 'summary'])).optional(),
97
+ detail_level: zod.z.enum(['brief', 'detailed', 'comprehensive']).optional()
98
+ });
99
+ // ========================================
100
+ // Intelligence Feature Types (v2.0)
101
+ // ========================================
102
+ /**
103
+ * Chunking strategies for content preprocessing
104
+ */
105
+ const CHUNKING_STRATEGIES = ['semantic', 'fixed-size', 'paragraph', 'sentence', 'code-block'];
106
+ /**
107
+ * Content types detected or specified
108
+ */
109
+ const CONTENT_TYPES = ['text', 'code', 'markdown', 'json', 'yaml'];
110
+ // ========================================
111
+ // Enhanced Search Types
112
+ // ========================================
113
+ /**
114
+ * Search modes for memory queries
115
+ */
116
+ const SEARCH_MODES = ['vector', 'text', 'hybrid'];
117
+ // ========================================
118
+ // Validation Schemas for Intelligence
119
+ // ========================================
120
+ const preprocessingOptionsSchema = zod.z.object({
121
+ chunking: zod.z.object({
122
+ strategy: zod.z.enum(CHUNKING_STRATEGIES).optional(),
123
+ maxChunkSize: zod.z.number().int().min(100).max(10000).optional(),
124
+ overlap: zod.z.number().int().min(0).max(500).optional()
125
+ }).optional(),
126
+ cleanContent: zod.z.boolean().optional(),
127
+ extractMetadata: zod.z.boolean().optional()
128
+ }).optional();
129
+ const enhancedSearchSchema = zod.z.object({
130
+ query: zod.z.string().min(1).max(1000),
131
+ type: zod.z.enum(MEMORY_TYPES).optional(),
132
+ threshold: zod.z.number().min(0).max(1).default(0.7),
133
+ limit: zod.z.number().int().min(1).max(100).default(20),
134
+ search_mode: zod.z.enum(SEARCH_MODES).default('hybrid'),
135
+ filters: zod.z.object({
136
+ tags: zod.z.array(zod.z.string()).optional(),
137
+ project_id: zod.z.string().uuid().optional(),
138
+ topic_id: zod.z.string().uuid().optional(),
139
+ date_range: zod.z.object({
140
+ from: zod.z.string().optional(),
141
+ to: zod.z.string().optional()
142
+ }).optional()
143
+ }).optional(),
144
+ include_chunks: zod.z.boolean().default(false)
145
+ });
146
+ const analyticsDateRangeSchema = zod.z.object({
147
+ from: zod.z.string().optional(),
148
+ to: zod.z.string().optional(),
149
+ group_by: zod.z.enum(['day', 'week', 'month']).default('day')
150
+ });
151
+
152
+ /**
153
+ * Core Utilities for Memory Client
154
+ * Browser-safe, no Node.js dependencies
155
+ */
156
+ /**
157
+ * Safely parse JSON with detailed error reporting
158
+ * Prevents scattered try/catch blocks throughout the codebase
159
+ */
160
+ function safeJsonParse(input) {
161
+ try {
162
+ const data = JSON.parse(input);
163
+ return { success: true, data };
164
+ }
165
+ catch (error) {
166
+ const message = error instanceof Error
167
+ ? error.message
168
+ : 'Unknown JSON parse error';
169
+ return { success: false, error: `Invalid JSON: ${message}` };
170
+ }
171
+ }
172
+ /**
173
+ * HTTP status code to error code mapping
174
+ */
175
+ function httpStatusToErrorCode(status) {
176
+ switch (status) {
177
+ case 400:
178
+ return 'VALIDATION_ERROR';
179
+ case 401:
180
+ return 'AUTH_ERROR';
181
+ case 403:
182
+ return 'FORBIDDEN';
183
+ case 404:
184
+ return 'NOT_FOUND';
185
+ case 408:
186
+ return 'TIMEOUT_ERROR';
187
+ case 409:
188
+ return 'CONFLICT';
189
+ case 429:
190
+ return 'RATE_LIMIT_ERROR';
191
+ case 500:
192
+ case 502:
193
+ case 503:
194
+ case 504:
195
+ return 'SERVER_ERROR';
196
+ default:
197
+ return 'API_ERROR';
198
+ }
199
+ }
200
+ /**
201
+ * Create a standardized error response from various error sources
202
+ */
203
+ function createErrorResponse(message, code = 'API_ERROR', statusCode, details) {
204
+ return {
205
+ code,
206
+ message,
207
+ statusCode,
208
+ details,
209
+ timestamp: new Date().toISOString()
210
+ };
211
+ }
212
+ /**
213
+ * Create an error response from an HTTP response
214
+ */
215
+ function createErrorFromResponse(status, statusText, body) {
216
+ const code = httpStatusToErrorCode(status);
217
+ // Try to extract message from response body
218
+ let message = `HTTP ${status}: ${statusText}`;
219
+ let details = undefined;
220
+ if (body && typeof body === 'object') {
221
+ const bodyObj = body;
222
+ if (typeof bodyObj.error === 'string') {
223
+ message = bodyObj.error;
224
+ }
225
+ else if (typeof bodyObj.message === 'string') {
226
+ message = bodyObj.message;
227
+ }
228
+ if (bodyObj.details) {
229
+ details = bodyObj.details;
230
+ }
231
+ }
232
+ return createErrorResponse(message, code, status, details);
233
+ }
234
+ /**
235
+ * Sleep utility for retry logic
236
+ */
237
+ function sleep(ms) {
238
+ return new Promise(resolve => setTimeout(resolve, ms));
239
+ }
240
+ /**
241
+ * Calculate retry delay with exponential backoff and jitter
242
+ */
243
+ function calculateRetryDelay(attempt, baseDelay = 1000, backoff = 'exponential', maxDelay = 30000) {
244
+ let delay;
245
+ if (backoff === 'exponential') {
246
+ delay = baseDelay * Math.pow(2, attempt);
247
+ }
248
+ else {
249
+ delay = baseDelay * (attempt + 1);
250
+ }
251
+ // Add jitter (±20%) to prevent thundering herd
252
+ const jitter = delay * 0.2 * (Math.random() * 2 - 1);
253
+ delay = Math.min(delay + jitter, maxDelay);
254
+ return Math.round(delay);
255
+ }
256
+ /**
257
+ * Check if an error is retryable based on status code
258
+ */
259
+ function isRetryableError(statusCode) {
260
+ if (!statusCode)
261
+ return true; // Network errors are retryable
262
+ // Retry on server errors and rate limits
263
+ return statusCode >= 500 || statusCode === 429 || statusCode === 408;
264
+ }
265
+
266
+ /**
267
+ * Core Memory Client - Pure Browser-Safe Implementation
268
+ *
269
+ * NO Node.js dependencies, NO CLI code, NO child_process
270
+ * Works in: Browser, React Native, Cloudflare Workers, Edge Functions, Deno, Bun
271
+ *
272
+ * Bundle size: ~15KB gzipped
273
+ */
274
+ /**
275
+ * Helper to check if response has error
276
+ */
277
+ function hasError(response) {
278
+ return response.error !== undefined;
279
+ }
280
+ /**
281
+ * Helper to check if response has data
282
+ */
283
+ function hasData(response) {
284
+ return response.data !== undefined;
285
+ }
286
+ /**
287
+ * Core Memory Client class for interacting with the Memory as a Service API
288
+ *
289
+ * This is a pure browser-safe client with zero Node.js dependencies.
290
+ * It uses only standard web APIs (fetch, AbortController, etc.)
291
+ */
292
+ class CoreMemoryClient {
293
+ constructor(config) {
294
+ this.config = {
295
+ timeout: 30000,
296
+ ...config
297
+ };
298
+ this.baseHeaders = {
299
+ 'Content-Type': 'application/json',
300
+ 'User-Agent': '@lanonasis/memory-client/2.0.0',
301
+ 'X-Project-Scope': 'lanonasis-maas', // Required by backend auth middleware
302
+ ...config.headers
303
+ };
304
+ // Set authentication headers
305
+ if (config.authToken) {
306
+ this.baseHeaders['Authorization'] = `Bearer ${config.authToken}`;
307
+ }
308
+ else if (config.apiKey) {
309
+ this.baseHeaders['X-API-Key'] = config.apiKey;
310
+ }
311
+ // Add organization ID header if provided
312
+ if (config.organizationId) {
313
+ this.baseHeaders['X-Organization-ID'] = config.organizationId;
314
+ }
315
+ }
316
+ /**
317
+ * Enrich request body with organization context if configured
318
+ * This ensures the API has the organization_id even if not in auth token
319
+ */
320
+ enrichWithOrgContext(body) {
321
+ // If organizationId is configured, include it in the request body
322
+ if (this.config.organizationId && !body.organization_id) {
323
+ return {
324
+ ...body,
325
+ organization_id: this.config.organizationId
326
+ };
327
+ }
328
+ // Fallback to userId if no organizationId configured
329
+ if (!this.config.organizationId && this.config.userId && !body.organization_id) {
330
+ return {
331
+ ...body,
332
+ organization_id: this.config.userId
333
+ };
334
+ }
335
+ return body;
336
+ }
337
+ /**
338
+ * Make an HTTP request to the API with retry support
339
+ */
340
+ async request(endpoint, options = {}) {
341
+ const startTime = Date.now();
342
+ const maxRetries = this.config.retry?.maxRetries ?? 3;
343
+ const baseDelay = this.config.retry?.retryDelay ?? 1000;
344
+ const backoff = this.config.retry?.backoff ?? 'exponential';
345
+ // Call onRequest hook if provided
346
+ if (this.config.onRequest) {
347
+ try {
348
+ this.config.onRequest(endpoint);
349
+ }
350
+ catch (error) {
351
+ console.warn('onRequest hook error:', error);
352
+ }
353
+ }
354
+ // Handle gateway vs direct API URL formatting
355
+ const baseUrl = this.config.apiUrl.includes('/api')
356
+ ? this.config.apiUrl.replace('/api', '')
357
+ : this.config.apiUrl;
358
+ const url = `${baseUrl}/api/v1${endpoint}`;
359
+ let lastError;
360
+ let attempt = 0;
361
+ while (attempt <= maxRetries) {
362
+ try {
363
+ const controller = new AbortController();
364
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
365
+ const response = await fetch(url, {
366
+ headers: { ...this.baseHeaders, ...options.headers },
367
+ signal: controller.signal,
368
+ ...options,
369
+ });
370
+ clearTimeout(timeoutId);
371
+ let data;
372
+ const contentType = response.headers.get('content-type');
373
+ if (contentType && contentType.includes('application/json')) {
374
+ data = await response.json();
375
+ }
376
+ else {
377
+ data = await response.text();
378
+ }
379
+ if (!response.ok) {
380
+ const error = createErrorFromResponse(response.status, response.statusText, data);
381
+ // Only retry on retryable errors (5xx, 429, 408)
382
+ if (isRetryableError(response.status) && attempt < maxRetries) {
383
+ lastError = error;
384
+ const delay = calculateRetryDelay(attempt, baseDelay, backoff);
385
+ await sleep(delay);
386
+ attempt++;
387
+ continue;
388
+ }
389
+ // Call onError hook if provided
390
+ if (this.config.onError) {
391
+ try {
392
+ this.config.onError(error);
393
+ }
394
+ catch (hookError) {
395
+ console.warn('onError hook error:', hookError);
396
+ }
397
+ }
398
+ return { error, meta: { duration: Date.now() - startTime, retries: attempt } };
399
+ }
400
+ // Call onResponse hook if provided
401
+ if (this.config.onResponse) {
402
+ try {
403
+ const duration = Date.now() - startTime;
404
+ this.config.onResponse(endpoint, duration);
405
+ }
406
+ catch (error) {
407
+ console.warn('onResponse hook error:', error);
408
+ }
409
+ }
410
+ return { data, meta: { duration: Date.now() - startTime, retries: attempt } };
411
+ }
412
+ catch (error) {
413
+ if (error instanceof Error && error.name === 'AbortError') {
414
+ const timeoutError = createErrorResponse('Request timeout', 'TIMEOUT_ERROR', 408);
415
+ // Retry on timeout
416
+ if (attempt < maxRetries) {
417
+ lastError = timeoutError;
418
+ const delay = calculateRetryDelay(attempt, baseDelay, backoff);
419
+ await sleep(delay);
420
+ attempt++;
421
+ continue;
422
+ }
423
+ if (this.config.onError) {
424
+ try {
425
+ this.config.onError(timeoutError);
426
+ }
427
+ catch (hookError) {
428
+ console.warn('onError hook error:', hookError);
429
+ }
430
+ }
431
+ return { error: timeoutError, meta: { duration: Date.now() - startTime, retries: attempt } };
432
+ }
433
+ const networkError = createErrorResponse(error instanceof Error ? error.message : 'Network error', 'NETWORK_ERROR');
434
+ // Retry on network errors
435
+ if (attempt < maxRetries) {
436
+ lastError = networkError;
437
+ const delay = calculateRetryDelay(attempt, baseDelay, backoff);
438
+ await sleep(delay);
439
+ attempt++;
440
+ continue;
441
+ }
442
+ if (this.config.onError) {
443
+ try {
444
+ this.config.onError(networkError);
445
+ }
446
+ catch (hookError) {
447
+ console.warn('onError hook error:', hookError);
448
+ }
449
+ }
450
+ return { error: networkError, meta: { duration: Date.now() - startTime, retries: attempt } };
451
+ }
452
+ }
453
+ // Should never reach here, but handle it gracefully
454
+ return {
455
+ error: lastError ?? createErrorResponse('Max retries exceeded', 'API_ERROR'),
456
+ meta: { duration: Date.now() - startTime, retries: attempt }
457
+ };
458
+ }
459
+ /**
460
+ * Validate input using Zod schema and return validation error if invalid
461
+ */
462
+ validateInput(schema, data) {
463
+ const result = schema.safeParse(data);
464
+ if (!result.success) {
465
+ // Extract error details from Zod error
466
+ const zodError = result.error;
467
+ const details = zodError?.issues?.map(issue => ({
468
+ field: issue.path.map(String).join('.'),
469
+ message: issue.message
470
+ })) ?? [];
471
+ return {
472
+ error: createErrorResponse('Validation failed', 'VALIDATION_ERROR', 400, details)
473
+ };
474
+ }
475
+ return null;
476
+ }
477
+ /**
478
+ * Test the API connection and authentication
479
+ */
480
+ async healthCheck() {
481
+ return this.request('/health');
482
+ }
483
+ // Memory Operations
484
+ /**
485
+ * Create a new memory with validation
486
+ */
487
+ async createMemory(memory) {
488
+ // Validate input before making request
489
+ const validationError = this.validateInput(createMemorySchema, memory);
490
+ if (validationError) {
491
+ return { error: validationError.error };
492
+ }
493
+ const enrichedMemory = this.enrichWithOrgContext(memory);
494
+ return this.request('/memory', {
495
+ method: 'POST',
496
+ body: JSON.stringify(enrichedMemory)
497
+ });
498
+ }
499
+ /**
500
+ * Get a memory by ID
501
+ */
502
+ async getMemory(id) {
503
+ return this.request(`/memory/${encodeURIComponent(id)}`);
504
+ }
505
+ /**
506
+ * Update an existing memory with validation
507
+ */
508
+ async updateMemory(id, updates) {
509
+ // Validate input before making request
510
+ const validationError = this.validateInput(updateMemorySchema, updates);
511
+ if (validationError) {
512
+ return { error: validationError.error };
513
+ }
514
+ return this.request(`/memory/${encodeURIComponent(id)}`, {
515
+ method: 'PUT',
516
+ body: JSON.stringify(updates)
517
+ });
518
+ }
519
+ /**
520
+ * Delete a memory
521
+ */
522
+ async deleteMemory(id) {
523
+ return this.request(`/memory/${encodeURIComponent(id)}`, {
524
+ method: 'DELETE'
525
+ });
526
+ }
527
+ /**
528
+ * List memories with optional filtering and pagination
529
+ */
530
+ async listMemories(options = {}) {
531
+ const params = new URLSearchParams();
532
+ Object.entries(options).forEach(([key, value]) => {
533
+ if (value !== undefined && value !== null) {
534
+ if (Array.isArray(value)) {
535
+ params.append(key, value.join(','));
536
+ }
537
+ else {
538
+ params.append(key, String(value));
539
+ }
540
+ }
541
+ });
542
+ const queryString = params.toString();
543
+ const endpoint = queryString ? `/memory?${queryString}` : '/memory';
544
+ return this.request(endpoint);
545
+ }
546
+ /**
547
+ * Search memories using semantic search with validation
548
+ */
549
+ async searchMemories(request) {
550
+ // Validate input before making request
551
+ const validationError = this.validateInput(searchMemorySchema, request);
552
+ if (validationError) {
553
+ // Return error response (data will be undefined, only error is set)
554
+ return { error: validationError.error };
555
+ }
556
+ const enrichedRequest = this.enrichWithOrgContext(request);
557
+ return this.request('/memory/search', {
558
+ method: 'POST',
559
+ body: JSON.stringify(enrichedRequest)
560
+ });
561
+ }
562
+ /**
563
+ * Bulk delete multiple memories
564
+ */
565
+ async bulkDeleteMemories(memoryIds) {
566
+ const enrichedRequest = this.enrichWithOrgContext({ memory_ids: memoryIds });
567
+ return this.request('/memory/bulk/delete', {
568
+ method: 'POST',
569
+ body: JSON.stringify(enrichedRequest)
570
+ });
571
+ }
572
+ // Topic Operations
573
+ /**
574
+ * Create a new topic with validation
575
+ */
576
+ async createTopic(topic) {
577
+ // Validate input before making request
578
+ const validationError = this.validateInput(createTopicSchema, topic);
579
+ if (validationError) {
580
+ return { error: validationError.error };
581
+ }
582
+ const enrichedTopic = this.enrichWithOrgContext(topic);
583
+ return this.request('/topics', {
584
+ method: 'POST',
585
+ body: JSON.stringify(enrichedTopic)
586
+ });
587
+ }
588
+ /**
589
+ * Get all topics
590
+ */
591
+ async getTopics() {
592
+ return this.request('/topics');
593
+ }
594
+ /**
595
+ * Get a topic by ID
596
+ */
597
+ async getTopic(id) {
598
+ return this.request(`/topics/${encodeURIComponent(id)}`);
599
+ }
600
+ /**
601
+ * Update a topic
602
+ */
603
+ async updateTopic(id, updates) {
604
+ return this.request(`/topics/${encodeURIComponent(id)}`, {
605
+ method: 'PUT',
606
+ body: JSON.stringify(updates)
607
+ });
608
+ }
609
+ /**
610
+ * Delete a topic
611
+ */
612
+ async deleteTopic(id) {
613
+ return this.request(`/topics/${encodeURIComponent(id)}`, {
614
+ method: 'DELETE'
615
+ });
616
+ }
617
+ /**
618
+ * Get user memory statistics
619
+ */
620
+ async getMemoryStats() {
621
+ return this.request('/memory/stats');
622
+ }
623
+ // ========================================
624
+ // Intelligence Features (v2.0)
625
+ // ========================================
626
+ /**
627
+ * Create a memory with preprocessing options (chunking, intelligence extraction)
628
+ *
629
+ * @example
630
+ * ```typescript
631
+ * const result = await client.createMemoryWithPreprocessing({
632
+ * title: 'Auth System Docs',
633
+ * content: 'Long content...',
634
+ * memory_type: 'knowledge',
635
+ * preprocessing: {
636
+ * chunking: { strategy: 'semantic', maxChunkSize: 1000 },
637
+ * extractMetadata: true
638
+ * }
639
+ * });
640
+ * ```
641
+ */
642
+ async createMemoryWithPreprocessing(memory) {
643
+ // Validate base memory fields
644
+ const validationError = this.validateInput(createMemorySchema, memory);
645
+ if (validationError) {
646
+ return { error: validationError.error };
647
+ }
648
+ const enrichedMemory = this.enrichWithOrgContext(memory);
649
+ return this.request('/memory', {
650
+ method: 'POST',
651
+ body: JSON.stringify(enrichedMemory)
652
+ });
653
+ }
654
+ /**
655
+ * Update a memory with re-chunking and embedding regeneration
656
+ *
657
+ * @example
658
+ * ```typescript
659
+ * const result = await client.updateMemoryWithPreprocessing('mem_123', {
660
+ * content: 'Updated content...',
661
+ * rechunk: true,
662
+ * regenerate_embedding: true
663
+ * });
664
+ * ```
665
+ */
666
+ async updateMemoryWithPreprocessing(id, updates) {
667
+ const validationError = this.validateInput(updateMemorySchema, updates);
668
+ if (validationError) {
669
+ return { error: validationError.error };
670
+ }
671
+ return this.request(`/memory/${encodeURIComponent(id)}`, {
672
+ method: 'PUT',
673
+ body: JSON.stringify(updates)
674
+ });
675
+ }
676
+ /**
677
+ * Enhanced semantic search with hybrid mode (vector + text)
678
+ *
679
+ * @example
680
+ * ```typescript
681
+ * const result = await client.enhancedSearch({
682
+ * query: 'authentication flow',
683
+ * search_mode: 'hybrid',
684
+ * filters: { tags: ['auth'], project_id: 'proj_123' },
685
+ * include_chunks: true
686
+ * });
687
+ * ```
688
+ */
689
+ async enhancedSearch(request) {
690
+ const validationError = this.validateInput(enhancedSearchSchema, request);
691
+ if (validationError) {
692
+ return { error: validationError.error };
693
+ }
694
+ const enrichedRequest = this.enrichWithOrgContext(request);
695
+ return this.request('/memory/search', {
696
+ method: 'POST',
697
+ body: JSON.stringify(enrichedRequest)
698
+ });
699
+ }
700
+ // ========================================
701
+ // Analytics Operations
702
+ // ========================================
703
+ /**
704
+ * Get search analytics data
705
+ *
706
+ * @example
707
+ * ```typescript
708
+ * const analytics = await client.getSearchAnalytics({
709
+ * from: '2025-01-01',
710
+ * to: '2025-12-31',
711
+ * group_by: 'day'
712
+ * });
713
+ * ```
714
+ */
715
+ async getSearchAnalytics(options = {}) {
716
+ const validationError = this.validateInput(analyticsDateRangeSchema, options);
717
+ if (validationError) {
718
+ return { error: validationError.error };
719
+ }
720
+ const params = new URLSearchParams();
721
+ if (options.from)
722
+ params.append('from', options.from);
723
+ if (options.to)
724
+ params.append('to', options.to);
725
+ if (options.group_by)
726
+ params.append('group_by', options.group_by);
727
+ const queryString = params.toString();
728
+ const endpoint = queryString ? `/analytics/search?${queryString}` : '/analytics/search';
729
+ return this.request(endpoint);
730
+ }
731
+ /**
732
+ * Get memory access patterns
733
+ *
734
+ * @example
735
+ * ```typescript
736
+ * const patterns = await client.getAccessPatterns({
737
+ * from: '2025-01-01',
738
+ * to: '2025-12-31'
739
+ * });
740
+ * console.log(patterns.data?.most_accessed);
741
+ * ```
742
+ */
743
+ async getAccessPatterns(options = {}) {
744
+ const params = new URLSearchParams();
745
+ if (options.from)
746
+ params.append('from', options.from);
747
+ if (options.to)
748
+ params.append('to', options.to);
749
+ const queryString = params.toString();
750
+ const endpoint = queryString ? `/analytics/access?${queryString}` : '/analytics/access';
751
+ return this.request(endpoint);
752
+ }
753
+ /**
754
+ * Get extended memory statistics with storage and activity metrics
755
+ *
756
+ * @example
757
+ * ```typescript
758
+ * const stats = await client.getExtendedStats();
759
+ * console.log(`Total chunks: ${stats.data?.storage.total_chunks}`);
760
+ * console.log(`Created today: ${stats.data?.activity.created_today}`);
761
+ * ```
762
+ */
763
+ async getExtendedStats() {
764
+ return this.request('/analytics/stats');
765
+ }
766
+ /**
767
+ * Get topic with its memories
768
+ *
769
+ * @example
770
+ * ```typescript
771
+ * const topic = await client.getTopicWithMemories('topic_123');
772
+ * console.log(topic.data?.memories);
773
+ * ```
774
+ */
775
+ async getTopicWithMemories(topicId, options = {}) {
776
+ const params = new URLSearchParams();
777
+ if (options.limit)
778
+ params.append('limit', String(options.limit));
779
+ if (options.offset)
780
+ params.append('offset', String(options.offset));
781
+ const queryString = params.toString();
782
+ const endpoint = queryString
783
+ ? `/topics/${encodeURIComponent(topicId)}/memories?${queryString}`
784
+ : `/topics/${encodeURIComponent(topicId)}/memories`;
785
+ return this.request(endpoint);
786
+ }
787
+ /**
788
+ * Get topics in hierarchical structure
789
+ *
790
+ * @example
791
+ * ```typescript
792
+ * const topics = await client.getTopicsHierarchy();
793
+ * // Returns nested topic tree with children
794
+ * ```
795
+ */
796
+ async getTopicsHierarchy() {
797
+ return this.request('/topics?include_hierarchy=true');
798
+ }
799
+ // Utility Methods
800
+ /**
801
+ * Update authentication token
802
+ */
803
+ setAuthToken(token) {
804
+ this.baseHeaders['Authorization'] = `Bearer ${token}`;
805
+ delete this.baseHeaders['X-API-Key'];
806
+ }
807
+ /**
808
+ * Update API key
809
+ */
810
+ setApiKey(apiKey) {
811
+ this.baseHeaders['X-API-Key'] = apiKey;
812
+ delete this.baseHeaders['Authorization'];
813
+ }
814
+ /**
815
+ * Clear authentication
816
+ */
817
+ clearAuth() {
818
+ delete this.baseHeaders['Authorization'];
819
+ delete this.baseHeaders['X-API-Key'];
820
+ }
821
+ /**
822
+ * Update configuration
823
+ */
824
+ updateConfig(updates) {
825
+ this.config = { ...this.config, ...updates };
826
+ if (updates.headers) {
827
+ this.baseHeaders = { ...this.baseHeaders, ...updates.headers };
828
+ }
829
+ }
830
+ /**
831
+ * Get current configuration (excluding sensitive data)
832
+ */
833
+ getConfig() {
834
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
835
+ const { apiKey, authToken, ...safeConfig } = this.config;
836
+ return safeConfig;
837
+ }
838
+ }
839
+ /**
840
+ * Factory function to create a new Core Memory Client instance
841
+ */
842
+ function createMemoryClient(config) {
843
+ return new CoreMemoryClient(config);
844
+ }
845
+
846
+ /**
847
+ * Error handling for Memory Client
848
+ * Browser-safe, no Node.js dependencies
849
+ */
850
+ /**
851
+ * Standardized error codes for programmatic error handling
852
+ */
853
+ const ERROR_CODES = [
854
+ 'API_ERROR',
855
+ 'AUTH_ERROR',
856
+ 'VALIDATION_ERROR',
857
+ 'TIMEOUT_ERROR',
858
+ 'RATE_LIMIT_ERROR',
859
+ 'NOT_FOUND',
860
+ 'NETWORK_ERROR',
861
+ 'FORBIDDEN',
862
+ 'CONFLICT',
863
+ 'SERVER_ERROR'
864
+ ];
865
+ /**
866
+ * Type guard to check if an object is an ApiErrorResponse
867
+ */
868
+ function isApiErrorResponse(value) {
869
+ return (typeof value === 'object' &&
870
+ value !== null &&
871
+ 'code' in value &&
872
+ 'message' in value &&
873
+ typeof value.code === 'string' &&
874
+ typeof value.message === 'string');
875
+ }
876
+ /**
877
+ * Base error class for Memory Client errors
878
+ */
879
+ class MemoryClientError extends Error {
880
+ constructor(message, code = 'API_ERROR', statusCode, details) {
881
+ super(message);
882
+ this.code = code;
883
+ this.statusCode = statusCode;
884
+ this.details = details;
885
+ this.name = 'MemoryClientError';
886
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
887
+ if (Error.captureStackTrace) {
888
+ Error.captureStackTrace(this, MemoryClientError);
889
+ }
890
+ }
891
+ /**
892
+ * Convert to ApiErrorResponse for consistent API responses
893
+ */
894
+ toResponse() {
895
+ return {
896
+ code: this.code,
897
+ message: this.message,
898
+ statusCode: this.statusCode,
899
+ details: this.details,
900
+ timestamp: new Date().toISOString()
901
+ };
902
+ }
903
+ }
904
+ /**
905
+ * Network/API error
906
+ */
907
+ class ApiError extends MemoryClientError {
908
+ constructor(message, statusCode, details) {
909
+ super(message, 'API_ERROR', statusCode, details);
910
+ this.name = 'ApiError';
911
+ }
912
+ /**
913
+ * Create from an HTTP response
914
+ */
915
+ static fromResponse(status, statusText, body) {
916
+ let message = `HTTP ${status}: ${statusText}`;
917
+ let details = undefined;
918
+ if (body && typeof body === 'object') {
919
+ const bodyObj = body;
920
+ if (typeof bodyObj.error === 'string') {
921
+ message = bodyObj.error;
922
+ }
923
+ else if (typeof bodyObj.message === 'string') {
924
+ message = bodyObj.message;
925
+ }
926
+ if (bodyObj.details) {
927
+ details = bodyObj.details;
928
+ }
929
+ }
930
+ return new ApiError(message, status, details);
931
+ }
932
+ }
933
+ /**
934
+ * Authentication error
935
+ */
936
+ class AuthenticationError extends MemoryClientError {
937
+ constructor(message = 'Authentication required') {
938
+ super(message, 'AUTH_ERROR', 401);
939
+ this.name = 'AuthenticationError';
940
+ }
941
+ }
942
+ /**
943
+ * Validation error with field-level details
944
+ */
945
+ class ValidationError extends MemoryClientError {
946
+ constructor(message, details) {
947
+ super(message, 'VALIDATION_ERROR', 400, details);
948
+ this.name = 'ValidationError';
949
+ // Parse validation details into field errors
950
+ this.validationErrors = [];
951
+ if (Array.isArray(details)) {
952
+ this.validationErrors = details.filter((item) => typeof item === 'object' &&
953
+ item !== null &&
954
+ typeof item.field === 'string' &&
955
+ typeof item.message === 'string');
956
+ }
957
+ }
958
+ /**
959
+ * Create from Zod error
960
+ */
961
+ static fromZodError(error) {
962
+ const details = error.issues.map(issue => ({
963
+ field: issue.path.join('.'),
964
+ message: issue.message
965
+ }));
966
+ return new ValidationError('Validation failed', details);
967
+ }
968
+ }
969
+ /**
970
+ * Timeout error
971
+ */
972
+ class TimeoutError extends MemoryClientError {
973
+ constructor(message = 'Request timeout') {
974
+ super(message, 'TIMEOUT_ERROR', 408);
975
+ this.name = 'TimeoutError';
976
+ }
977
+ }
978
+ /**
979
+ * Rate limit error with retry-after info
980
+ */
981
+ class RateLimitError extends MemoryClientError {
982
+ constructor(message = 'Rate limit exceeded', retryAfter) {
983
+ super(message, 'RATE_LIMIT_ERROR', 429, { retryAfter });
984
+ this.name = 'RateLimitError';
985
+ this.retryAfter = retryAfter;
986
+ }
987
+ }
988
+ /**
989
+ * Not found error
990
+ */
991
+ class NotFoundError extends MemoryClientError {
992
+ constructor(resource) {
993
+ super(`${resource} not found`, 'NOT_FOUND', 404);
994
+ this.name = 'NotFoundError';
995
+ this.resource = resource;
996
+ }
997
+ }
998
+ /**
999
+ * Network error (no response received)
1000
+ */
1001
+ class NetworkError extends MemoryClientError {
1002
+ constructor(message = 'Network error') {
1003
+ super(message, 'NETWORK_ERROR');
1004
+ this.name = 'NetworkError';
1005
+ }
1006
+ }
1007
+ /**
1008
+ * Server error (5xx responses)
1009
+ */
1010
+ class ServerError extends MemoryClientError {
1011
+ constructor(message, statusCode = 500) {
1012
+ super(message, 'SERVER_ERROR', statusCode);
1013
+ this.name = 'ServerError';
1014
+ }
1015
+ }
1016
+ /**
1017
+ * Create appropriate error class from status code
1018
+ */
1019
+ function createErrorFromStatus(status, message, details) {
1020
+ switch (status) {
1021
+ case 400:
1022
+ return new ValidationError(message, details);
1023
+ case 401:
1024
+ return new AuthenticationError(message);
1025
+ case 404:
1026
+ return new NotFoundError(message);
1027
+ case 408:
1028
+ return new TimeoutError(message);
1029
+ case 429:
1030
+ return new RateLimitError(message);
1031
+ case 500:
1032
+ case 502:
1033
+ case 503:
1034
+ case 504:
1035
+ return new ServerError(message, status);
1036
+ default:
1037
+ return new ApiError(message, status, details);
1038
+ }
1039
+ }
1040
+
1041
+ /**
1042
+ * @lanonasis/memory-client
1043
+ *
1044
+ * Universal Memory as a Service (MaaS) Client SDK for Lanonasis
1045
+ * Intelligent memory management with semantic search capabilities
1046
+ *
1047
+ * v2.0.0 - Universal SDK Redesign
1048
+ * "Drop In and Sleep" Architecture - Works everywhere with zero configuration
1049
+ *
1050
+ * @example Browser/Web App
1051
+ * ```ts
1052
+ * import { createMemoryClient } from '@lanonasis/memory-client/core';
1053
+ * const client = createMemoryClient({ apiKey: 'your-key' });
1054
+ * ```
1055
+ *
1056
+ * @example Node.js
1057
+ * ```ts
1058
+ * import { createNodeMemoryClient } from '@lanonasis/memory-client/node';
1059
+ * const client = await createNodeMemoryClient({ apiKey: process.env.KEY });
1060
+ * ```
1061
+ *
1062
+ * @example React
1063
+ * ```tsx
1064
+ * import { MemoryProvider, useMemories } from '@lanonasis/memory-client/react';
1065
+ * ```
1066
+ *
1067
+ * @example Vue
1068
+ * ```ts
1069
+ * import { createMemoryPlugin, useMemories } from '@lanonasis/memory-client/vue';
1070
+ * ```
1071
+ */
1072
+ // ========================================
1073
+ // Internal Imports (for use in this file)
1074
+ // ========================================
1075
+ // ========================================
1076
+ // Constants
1077
+ // ========================================
1078
+ const VERSION = '2.0.0';
1079
+ const CLIENT_NAME = '@lanonasis/memory-client';
1080
+ // ========================================
1081
+ // Environment Detection
1082
+ // ========================================
1083
+ const isBrowser = typeof window !== 'undefined';
1084
+ const isNode = typeof globalThis !== 'undefined' && 'process' in globalThis && globalThis.process?.versions?.node;
1085
+ const isEdge = !isBrowser && !isNode;
1086
+ /**
1087
+ * Get the current runtime environment
1088
+ */
1089
+ function getEnvironment() {
1090
+ if (isBrowser)
1091
+ return 'browser';
1092
+ if (isNode)
1093
+ return 'node';
1094
+ return 'edge';
1095
+ }
1096
+ /**
1097
+ * Auto-detecting client factory - "Drop In and Sleep" architecture
1098
+ *
1099
+ * Automatically detects the runtime environment and returns the appropriate client:
1100
+ * - Browser/Edge: Returns CoreMemoryClient (lightweight, browser-safe)
1101
+ * - Node.js: Returns EnhancedMemoryClient (with CLI/MCP support)
1102
+ *
1103
+ * @example
1104
+ * ```typescript
1105
+ * import { createClient } from '@lanonasis/memory-client';
1106
+ *
1107
+ * // Works in any environment!
1108
+ * const client = await createClient({
1109
+ * apiUrl: 'https://api.lanonasis.com',
1110
+ * apiKey: 'your-key'
1111
+ * });
1112
+ *
1113
+ * const memories = await client.listMemories();
1114
+ * ```
1115
+ */
1116
+ async function createClient(config) {
1117
+ const environment = getEnvironment();
1118
+ if (environment === 'node') {
1119
+ try {
1120
+ // Dynamic import for Node.js client to avoid bundling in browser
1121
+ const { createNodeMemoryClient } = await Promise.resolve().then(function () { return index; });
1122
+ return await createNodeMemoryClient({
1123
+ ...config,
1124
+ preferCLI: config.preferCLI ?? true,
1125
+ enableMCP: config.enableMCP ?? true
1126
+ });
1127
+ }
1128
+ catch {
1129
+ // Fallback to core client if Node module fails to load
1130
+ console.warn('Failed to load Node.js client, falling back to core client');
1131
+ }
1132
+ }
1133
+ // Browser, Edge, or fallback
1134
+ const clientConfig = {
1135
+ apiUrl: config.apiUrl,
1136
+ apiKey: config.apiKey,
1137
+ authToken: config.authToken,
1138
+ organizationId: config.organizationId,
1139
+ timeout: config.timeout ?? (environment === 'edge' ? 5000 : 30000),
1140
+ headers: config.headers,
1141
+ retry: config.retry
1142
+ };
1143
+ return createMemoryClient(clientConfig);
1144
+ }
1145
+ // ========================================
1146
+ // Default Configurations
1147
+ // ========================================
1148
+ const defaultConfigs = {
1149
+ development: {
1150
+ apiUrl: 'http://localhost:3001',
1151
+ timeout: 30000,
1152
+ },
1153
+ production: {
1154
+ apiUrl: 'https://api.lanonasis.com',
1155
+ timeout: 15000,
1156
+ },
1157
+ edge: {
1158
+ apiUrl: 'https://api.lanonasis.com',
1159
+ timeout: 5000,
1160
+ }
1161
+ };
1162
+ // Note: Enhanced client requires Node.js, so we don't export it from main entry
1163
+ // Users should import from '@lanonasis/memory-client/node' instead
1164
+ // ========================================
1165
+ // Usage Instructions
1166
+ // ========================================
1167
+ /**
1168
+ * # @lanonasis/memory-client v2.0
1169
+ *
1170
+ * ## Quick Start
1171
+ *
1172
+ * ### Browser / Web App
1173
+ * ```bash
1174
+ * npm install @lanonasis/memory-client
1175
+ * ```
1176
+ * ```typescript
1177
+ * import { createMemoryClient } from '@lanonasis/memory-client/core';
1178
+ *
1179
+ * const client = createMemoryClient({
1180
+ * apiUrl: 'https://api.lanonasis.com',
1181
+ * apiKey: 'your-key-here'
1182
+ * });
1183
+ *
1184
+ * const memories = await client.listMemories();
1185
+ * ```
1186
+ *
1187
+ * ### Node.js with CLI Support
1188
+ * ```typescript
1189
+ * import { createNodeMemoryClient } from '@lanonasis/memory-client/node';
1190
+ *
1191
+ * const client = await createNodeMemoryClient({
1192
+ * apiKey: process.env.LANONASIS_KEY,
1193
+ * preferCLI: true // Automatically uses CLI if available
1194
+ * });
1195
+ *
1196
+ * const result = await client.listMemories();
1197
+ * console.log(`Using: ${result.source}`); // 'cli' or 'api'
1198
+ * ```
1199
+ *
1200
+ * ### React
1201
+ * ```tsx
1202
+ * import { MemoryProvider, useMemories } from '@lanonasis/memory-client/react';
1203
+ *
1204
+ * function App() {
1205
+ * return (
1206
+ * <MemoryProvider apiKey="your-key">
1207
+ * <MemoryList />
1208
+ * </MemoryProvider>
1209
+ * );
1210
+ * }
1211
+ *
1212
+ * function MemoryList() {
1213
+ * const { memories, loading } = useMemories();
1214
+ * if (loading) return <div>Loading...</div>;
1215
+ * return <div>{memories.map(m => <div key={m.id}>{m.title}</div>)}</div>;
1216
+ * }
1217
+ * ```
1218
+ *
1219
+ * ### Vue 3
1220
+ * ```typescript
1221
+ * import { createMemoryPlugin, useMemories } from '@lanonasis/memory-client/vue';
1222
+ *
1223
+ * const app = createApp(App);
1224
+ * app.use(createMemoryPlugin({ apiKey: 'your-key' }));
1225
+ * ```
1226
+ *
1227
+ * ### Edge Functions (Cloudflare Workers, Vercel Edge)
1228
+ * ```typescript
1229
+ * import { createMemoryClient } from '@lanonasis/memory-client/core';
1230
+ * import { edgePreset } from '@lanonasis/memory-client/presets';
1231
+ *
1232
+ * export default {
1233
+ * async fetch(request: Request, env: Env) {
1234
+ * const client = createMemoryClient(edgePreset({
1235
+ * apiKey: env.LANONASIS_KEY
1236
+ * }));
1237
+ * const memories = await client.searchMemories({ query: 'test' });
1238
+ * return Response.json(memories.data);
1239
+ * }
1240
+ * };
1241
+ * ```
1242
+ *
1243
+ * ## Bundle Sizes
1244
+ *
1245
+ * - **Core** (browser): ~15KB gzipped
1246
+ * - **Node** (with CLI): ~35KB gzipped
1247
+ * - **React**: ~18KB gzipped (+ React)
1248
+ * - **Vue**: ~17KB gzipped (+ Vue)
1249
+ * - **Presets**: ~2KB gzipped
1250
+ *
1251
+ * ## Documentation
1252
+ *
1253
+ * - Full docs: https://docs.lanonasis.com/sdk
1254
+ * - API reference: https://docs.lanonasis.com/api
1255
+ * - Examples: https://github.com/lanonasis/examples
1256
+ */
1257
+
1258
+ /**
1259
+ * CLI Integration Module for Memory Client SDK
1260
+ *
1261
+ * Provides intelligent CLI detection and MCP channel utilization
1262
+ * when @lanonasis/cli v1.5.2+ is available in the environment
1263
+ *
1264
+ * IMPORTANT: This file imports Node.js modules and should only be used in Node.js environments
1265
+ */
1266
+ const execAsync = util.promisify(child_process.exec);
1267
+ /**
1268
+ * CLI Detection and Integration Service
1269
+ */
1270
+ class CLIIntegration {
1271
+ constructor() {
1272
+ this.cliInfo = null;
1273
+ this.detectionPromise = null;
1274
+ }
1275
+ /**
1276
+ * Detect if CLI is available and get its capabilities
1277
+ */
1278
+ async detectCLI() {
1279
+ // Return cached result if already detected
1280
+ if (this.cliInfo) {
1281
+ return this.cliInfo;
1282
+ }
1283
+ // Return existing promise if detection is in progress
1284
+ if (this.detectionPromise) {
1285
+ return this.detectionPromise;
1286
+ }
1287
+ // Start new detection
1288
+ this.detectionPromise = this.performDetection();
1289
+ this.cliInfo = await this.detectionPromise;
1290
+ return this.cliInfo;
1291
+ }
1292
+ async performDetection() {
1293
+ try {
1294
+ // Check if onasis/lanonasis CLI is available
1295
+ let versionOutput = '';
1296
+ try {
1297
+ const { stdout } = await execAsync('onasis --version 2>/dev/null', { timeout: 5000 });
1298
+ versionOutput = stdout;
1299
+ }
1300
+ catch {
1301
+ // Try lanonasis if onasis fails
1302
+ const { stdout } = await execAsync('lanonasis --version 2>/dev/null', { timeout: 5000 });
1303
+ versionOutput = stdout;
1304
+ }
1305
+ const version = versionOutput.trim();
1306
+ // Verify it's v1.5.2 or higher for Golden Contract support
1307
+ const versionMatch = version.match(/(\d+)\.(\d+)\.(\d+)/);
1308
+ if (!versionMatch) {
1309
+ return { available: false };
1310
+ }
1311
+ const [, major, minor, patch] = versionMatch.map(Number);
1312
+ const isCompatible = major > 1 || (major === 1 && minor > 5) || (major === 1 && minor === 5 && patch >= 2);
1313
+ if (!isCompatible) {
1314
+ return {
1315
+ available: true,
1316
+ version,
1317
+ mcpAvailable: false,
1318
+ authenticated: false
1319
+ };
1320
+ }
1321
+ // Check MCP availability
1322
+ let mcpAvailable = false;
1323
+ try {
1324
+ await execAsync('onasis mcp status --output json 2>/dev/null || lanonasis mcp status --output json 2>/dev/null', {
1325
+ timeout: 3000
1326
+ });
1327
+ mcpAvailable = true;
1328
+ }
1329
+ catch {
1330
+ // MCP not available or not configured
1331
+ }
1332
+ // Check authentication status
1333
+ let authenticated = false;
1334
+ try {
1335
+ const { stdout: authOutput } = await execAsync('onasis auth status --output json 2>/dev/null || lanonasis auth status --output json 2>/dev/null', {
1336
+ timeout: 3000
1337
+ });
1338
+ const parseResult = safeJsonParse(authOutput);
1339
+ if (parseResult.success) {
1340
+ authenticated = parseResult.data.authenticated === true;
1341
+ }
1342
+ }
1343
+ catch {
1344
+ // Authentication check failed
1345
+ }
1346
+ return {
1347
+ available: true,
1348
+ version,
1349
+ mcpAvailable,
1350
+ authenticated
1351
+ };
1352
+ }
1353
+ catch {
1354
+ return { available: false };
1355
+ }
1356
+ }
1357
+ /**
1358
+ * Execute CLI command and return parsed JSON result
1359
+ */
1360
+ async executeCLICommand(command, options = {}) {
1361
+ const cliInfo = await this.detectCLI();
1362
+ if (!cliInfo.available) {
1363
+ return { error: createErrorResponse('CLI not available', 'API_ERROR') };
1364
+ }
1365
+ if (!cliInfo.authenticated) {
1366
+ return { error: createErrorResponse('CLI not authenticated. Run: onasis login', 'AUTH_ERROR', 401) };
1367
+ }
1368
+ try {
1369
+ const timeout = options.timeout || 30000;
1370
+ const outputFormat = options.outputFormat || 'json';
1371
+ const verbose = options.verbose ? '--verbose' : '';
1372
+ // Determine which CLI command to use (prefer onasis for Golden Contract)
1373
+ const cliCmd = await this.getPreferredCLICommand();
1374
+ const fullCommand = `${cliCmd} ${command} --output ${outputFormat} ${verbose}`.trim();
1375
+ const { stdout, stderr } = await execAsync(fullCommand, {
1376
+ timeout,
1377
+ maxBuffer: 1024 * 1024 // 1MB buffer
1378
+ });
1379
+ if (stderr && stderr.trim()) {
1380
+ console.warn('CLI warning:', stderr);
1381
+ }
1382
+ if (outputFormat === 'json') {
1383
+ const parseResult = safeJsonParse(stdout);
1384
+ if (parseResult.success) {
1385
+ return { data: parseResult.data };
1386
+ }
1387
+ return { error: createErrorResponse(parseResult.error, 'VALIDATION_ERROR', 400) };
1388
+ }
1389
+ return { data: stdout };
1390
+ }
1391
+ catch (error) {
1392
+ if (error instanceof Error && error.message.includes('timeout')) {
1393
+ return { error: createErrorResponse('CLI command timeout', 'TIMEOUT_ERROR', 408) };
1394
+ }
1395
+ return {
1396
+ error: createErrorResponse(error instanceof Error ? error.message : 'CLI command failed', 'API_ERROR')
1397
+ };
1398
+ }
1399
+ }
1400
+ /**
1401
+ * Get preferred CLI command (onasis for Golden Contract, fallback to lanonasis)
1402
+ */
1403
+ async getPreferredCLICommand() {
1404
+ try {
1405
+ child_process.execSync('which onasis', { stdio: 'ignore', timeout: 1000 });
1406
+ return 'onasis';
1407
+ }
1408
+ catch {
1409
+ return 'lanonasis';
1410
+ }
1411
+ }
1412
+ /**
1413
+ * Memory operations via CLI
1414
+ */
1415
+ async createMemoryViaCLI(title, content, options = {}) {
1416
+ const { memoryType = 'context', tags = [], topicId } = options;
1417
+ let command = `memory create --title "${title}" --content "${content}" --memory-type ${memoryType}`;
1418
+ if (tags.length > 0) {
1419
+ command += ` --tags "${tags.join(',')}"`;
1420
+ }
1421
+ if (topicId) {
1422
+ command += ` --topic-id "${topicId}"`;
1423
+ }
1424
+ return this.executeCLICommand(command);
1425
+ }
1426
+ async listMemoriesViaCLI(options = {}) {
1427
+ let command = 'memory list';
1428
+ if (options.limit) {
1429
+ command += ` --limit ${options.limit}`;
1430
+ }
1431
+ if (options.memoryType) {
1432
+ command += ` --memory-type ${options.memoryType}`;
1433
+ }
1434
+ if (options.tags && options.tags.length > 0) {
1435
+ command += ` --tags "${options.tags.join(',')}"`;
1436
+ }
1437
+ if (options.sortBy) {
1438
+ command += ` --sort-by ${options.sortBy}`;
1439
+ }
1440
+ return this.executeCLICommand(command);
1441
+ }
1442
+ async searchMemoriesViaCLI(query, options = {}) {
1443
+ let command = `memory search "${query}"`;
1444
+ if (options.limit) {
1445
+ command += ` --limit ${options.limit}`;
1446
+ }
1447
+ if (options.memoryTypes && options.memoryTypes.length > 0) {
1448
+ command += ` --memory-types "${options.memoryTypes.join(',')}"`;
1449
+ }
1450
+ return this.executeCLICommand(command);
1451
+ }
1452
+ /**
1453
+ * Health check via CLI
1454
+ */
1455
+ async healthCheckViaCLI() {
1456
+ return this.executeCLICommand('health');
1457
+ }
1458
+ /**
1459
+ * MCP-specific operations
1460
+ */
1461
+ async getMCPStatus() {
1462
+ const cliInfo = await this.detectCLI();
1463
+ if (!cliInfo.mcpAvailable) {
1464
+ return { error: createErrorResponse('MCP not available via CLI', 'API_ERROR') };
1465
+ }
1466
+ return this.executeCLICommand('mcp status');
1467
+ }
1468
+ async listMCPTools() {
1469
+ const cliInfo = await this.detectCLI();
1470
+ if (!cliInfo.mcpAvailable) {
1471
+ return { error: createErrorResponse('MCP not available via CLI', 'API_ERROR') };
1472
+ }
1473
+ return this.executeCLICommand('mcp tools');
1474
+ }
1475
+ /**
1476
+ * Authentication operations
1477
+ */
1478
+ async getAuthStatus() {
1479
+ return this.executeCLICommand('auth status');
1480
+ }
1481
+ /**
1482
+ * Check if specific CLI features are available
1483
+ */
1484
+ async getCapabilities() {
1485
+ const cliInfo = await this.detectCLI();
1486
+ return {
1487
+ cliAvailable: cliInfo.available,
1488
+ version: cliInfo.version,
1489
+ mcpSupport: cliInfo.mcpAvailable || false,
1490
+ authenticated: cliInfo.authenticated || false,
1491
+ goldenContract: cliInfo.available && this.isGoldenContractCompliant(cliInfo.version)
1492
+ };
1493
+ }
1494
+ isGoldenContractCompliant(version) {
1495
+ if (!version)
1496
+ return false;
1497
+ const versionMatch = version.match(/(\d+)\.(\d+)\.(\d+)/);
1498
+ if (!versionMatch)
1499
+ return false;
1500
+ const [, major, minor, patch] = versionMatch.map(Number);
1501
+ return major > 1 || (major === 1 && minor > 5) || (major === 1 && minor === 5 && patch >= 2);
1502
+ }
1503
+ /**
1504
+ * Force refresh CLI detection
1505
+ */
1506
+ async refresh() {
1507
+ this.cliInfo = null;
1508
+ this.detectionPromise = null;
1509
+ return this.detectCLI();
1510
+ }
1511
+ /**
1512
+ * Get cached CLI info without re-detection
1513
+ */
1514
+ getCachedInfo() {
1515
+ return this.cliInfo;
1516
+ }
1517
+ }
1518
+
1519
+ /**
1520
+ * Enhanced Memory Client with CLI Integration
1521
+ *
1522
+ * Intelligently routes requests through CLI v1.5.2+ when available,
1523
+ * with fallback to direct API for maximum compatibility and performance
1524
+ *
1525
+ * IMPORTANT: This file uses Node.js-specific features (process.env) and should only be used in Node.js environments
1526
+ */
1527
+ /**
1528
+ * Enhanced Memory Client with intelligent CLI/API routing
1529
+ */
1530
+ class EnhancedMemoryClient {
1531
+ createDefaultCapabilities() {
1532
+ return {
1533
+ cliAvailable: false,
1534
+ mcpSupport: false,
1535
+ authenticated: false,
1536
+ goldenContract: false
1537
+ };
1538
+ }
1539
+ constructor(config) {
1540
+ this.capabilities = null;
1541
+ // Merge config with defaults, ensuring all required fields are present
1542
+ // Spread config first, then apply defaults only for undefined values
1543
+ const mergedConfig = {
1544
+ ...config,
1545
+ preferCLI: config.preferCLI ?? true,
1546
+ enableMCP: config.enableMCP ?? true,
1547
+ cliDetectionTimeout: config.cliDetectionTimeout ?? 5000,
1548
+ fallbackToAPI: config.fallbackToAPI ?? true,
1549
+ minCLIVersion: config.minCLIVersion ?? '1.5.2',
1550
+ verbose: config.verbose ?? false,
1551
+ timeout: config.timeout ?? 30000,
1552
+ apiUrl: config.apiUrl || 'https://api.lanonasis.com',
1553
+ apiKey: config.apiKey || process.env.LANONASIS_API_KEY || '',
1554
+ authToken: config.authToken || '',
1555
+ headers: config.headers || {}
1556
+ };
1557
+ this.config = mergedConfig;
1558
+ this.directClient = new CoreMemoryClient(config);
1559
+ this.cliIntegration = new CLIIntegration();
1560
+ }
1561
+ /**
1562
+ * Initialize the client and detect capabilities
1563
+ */
1564
+ async initialize() {
1565
+ try {
1566
+ const detectionPromise = this.cliIntegration.getCapabilities();
1567
+ const capabilities = this.config.cliDetectionTimeout > 0
1568
+ ? await Promise.race([
1569
+ detectionPromise,
1570
+ new Promise((resolve) => {
1571
+ setTimeout(() => resolve(null), this.config.cliDetectionTimeout);
1572
+ })
1573
+ ])
1574
+ : await detectionPromise;
1575
+ if (capabilities) {
1576
+ this.capabilities = capabilities;
1577
+ if (this.config.verbose && capabilities.cliAvailable && !capabilities.authenticated) {
1578
+ const suggestedCommand = capabilities.goldenContract ? 'onasis login' : 'lanonasis login';
1579
+ console.warn(`CLI detected but not authenticated. Run '${suggestedCommand}' to enable enhanced SDK features.`);
1580
+ }
1581
+ }
1582
+ else {
1583
+ this.capabilities = this.createDefaultCapabilities();
1584
+ if (this.config.verbose) {
1585
+ console.warn(`CLI detection timed out after ${this.config.cliDetectionTimeout}ms. Falling back to API mode.`);
1586
+ }
1587
+ }
1588
+ }
1589
+ catch (error) {
1590
+ if (this.config.verbose) {
1591
+ console.warn('CLI detection failed:', error);
1592
+ }
1593
+ this.capabilities = this.createDefaultCapabilities();
1594
+ }
1595
+ }
1596
+ /**
1597
+ * Get current capabilities
1598
+ */
1599
+ async getCapabilities() {
1600
+ if (!this.capabilities) {
1601
+ await this.initialize();
1602
+ }
1603
+ if (!this.capabilities) {
1604
+ this.capabilities = this.createDefaultCapabilities();
1605
+ }
1606
+ return this.capabilities;
1607
+ }
1608
+ /**
1609
+ * Determine if operation should use CLI
1610
+ */
1611
+ async shouldUseCLI() {
1612
+ const capabilities = await this.getCapabilities();
1613
+ return (this.config.preferCLI &&
1614
+ capabilities.cliAvailable &&
1615
+ capabilities.authenticated &&
1616
+ capabilities.goldenContract);
1617
+ }
1618
+ /**
1619
+ * Execute operation with intelligent routing
1620
+ */
1621
+ async executeOperation(operation, cliOperation, apiOperation) {
1622
+ const useCLI = await this.shouldUseCLI();
1623
+ const capabilities = await this.getCapabilities();
1624
+ if (useCLI) {
1625
+ try {
1626
+ const result = await cliOperation();
1627
+ if (result.error && this.config.fallbackToAPI) {
1628
+ console.warn(`CLI ${operation} failed, falling back to API:`, result.error);
1629
+ const apiResult = await apiOperation();
1630
+ return {
1631
+ ...apiResult,
1632
+ source: 'api',
1633
+ mcpUsed: false
1634
+ };
1635
+ }
1636
+ return {
1637
+ ...result,
1638
+ source: 'cli',
1639
+ mcpUsed: capabilities.mcpSupport
1640
+ };
1641
+ }
1642
+ catch (error) {
1643
+ if (this.config.fallbackToAPI) {
1644
+ console.warn(`CLI ${operation} error, falling back to API:`, error);
1645
+ const apiResult = await apiOperation();
1646
+ return {
1647
+ ...apiResult,
1648
+ source: 'api',
1649
+ mcpUsed: false
1650
+ };
1651
+ }
1652
+ return {
1653
+ error: createErrorResponse(error instanceof Error ? error.message : `CLI ${operation} failed`, 'API_ERROR'),
1654
+ source: 'cli',
1655
+ mcpUsed: false
1656
+ };
1657
+ }
1658
+ }
1659
+ else {
1660
+ const result = await apiOperation();
1661
+ return {
1662
+ ...result,
1663
+ source: 'api',
1664
+ mcpUsed: false
1665
+ };
1666
+ }
1667
+ }
1668
+ // Enhanced API Methods
1669
+ /**
1670
+ * Health check with intelligent routing
1671
+ */
1672
+ async healthCheck() {
1673
+ return this.executeOperation('health check', () => this.cliIntegration.healthCheckViaCLI(), () => this.directClient.healthCheck());
1674
+ }
1675
+ /**
1676
+ * Create memory with CLI/API routing
1677
+ */
1678
+ async createMemory(memory) {
1679
+ return this.executeOperation('create memory', () => this.cliIntegration.createMemoryViaCLI(memory.title, memory.content, {
1680
+ memoryType: memory.memory_type,
1681
+ tags: memory.tags,
1682
+ topicId: memory.topic_id
1683
+ }), () => this.directClient.createMemory(memory));
1684
+ }
1685
+ /**
1686
+ * List memories with intelligent routing
1687
+ */
1688
+ async listMemories(options = {}) {
1689
+ return this.executeOperation('list memories', () => this.cliIntegration.listMemoriesViaCLI({
1690
+ limit: options.limit,
1691
+ memoryType: options.memory_type,
1692
+ tags: options.tags,
1693
+ sortBy: options.sort
1694
+ }), () => this.directClient.listMemories(options));
1695
+ }
1696
+ /**
1697
+ * Search memories with MCP enhancement when available
1698
+ */
1699
+ async searchMemories(request) {
1700
+ return this.executeOperation('search memories', () => this.cliIntegration.searchMemoriesViaCLI(request.query, {
1701
+ limit: request.limit,
1702
+ memoryTypes: request.memory_types
1703
+ }), () => this.directClient.searchMemories(request));
1704
+ }
1705
+ /**
1706
+ * Get memory by ID (API only for now)
1707
+ */
1708
+ async getMemory(id) {
1709
+ // CLI doesn't have get by ID yet, use API
1710
+ const result = await this.directClient.getMemory(id);
1711
+ return {
1712
+ ...result,
1713
+ source: 'api',
1714
+ mcpUsed: false
1715
+ };
1716
+ }
1717
+ /**
1718
+ * Update memory (API only for now)
1719
+ */
1720
+ async updateMemory(id, updates) {
1721
+ // CLI doesn't have update yet, use API
1722
+ const result = await this.directClient.updateMemory(id, updates);
1723
+ return {
1724
+ ...result,
1725
+ source: 'api',
1726
+ mcpUsed: false
1727
+ };
1728
+ }
1729
+ /**
1730
+ * Delete memory (API only for now)
1731
+ */
1732
+ async deleteMemory(id) {
1733
+ // CLI doesn't have delete yet, use API
1734
+ const result = await this.directClient.deleteMemory(id);
1735
+ return {
1736
+ ...result,
1737
+ source: 'api',
1738
+ mcpUsed: false
1739
+ };
1740
+ }
1741
+ // Topic Operations (API only for now)
1742
+ async createTopic(topic) {
1743
+ const result = await this.directClient.createTopic(topic);
1744
+ return { ...result, source: 'api', mcpUsed: false };
1745
+ }
1746
+ async getTopics() {
1747
+ const result = await this.directClient.getTopics();
1748
+ return { ...result, source: 'api', mcpUsed: false };
1749
+ }
1750
+ async getTopic(id) {
1751
+ const result = await this.directClient.getTopic(id);
1752
+ return { ...result, source: 'api', mcpUsed: false };
1753
+ }
1754
+ async updateTopic(id, updates) {
1755
+ const result = await this.directClient.updateTopic(id, updates);
1756
+ return { ...result, source: 'api', mcpUsed: false };
1757
+ }
1758
+ async deleteTopic(id) {
1759
+ const result = await this.directClient.deleteTopic(id);
1760
+ return { ...result, source: 'api', mcpUsed: false };
1761
+ }
1762
+ /**
1763
+ * Get memory statistics
1764
+ */
1765
+ async getMemoryStats() {
1766
+ const result = await this.directClient.getMemoryStats();
1767
+ return { ...result, source: 'api', mcpUsed: false };
1768
+ }
1769
+ // Utility Methods
1770
+ /**
1771
+ * Force CLI re-detection
1772
+ */
1773
+ async refreshCLIDetection() {
1774
+ this.capabilities = null;
1775
+ await this.cliIntegration.refresh();
1776
+ await this.initialize();
1777
+ }
1778
+ /**
1779
+ * Get authentication status from CLI
1780
+ */
1781
+ async getAuthStatus() {
1782
+ try {
1783
+ const result = await this.cliIntegration.getAuthStatus();
1784
+ return { ...result, source: 'cli', mcpUsed: false };
1785
+ }
1786
+ catch (error) {
1787
+ return {
1788
+ error: createErrorResponse(error instanceof Error ? error.message : 'Auth status check failed', 'API_ERROR'),
1789
+ source: 'cli',
1790
+ mcpUsed: false
1791
+ };
1792
+ }
1793
+ }
1794
+ /**
1795
+ * Get MCP status when available
1796
+ */
1797
+ async getMCPStatus() {
1798
+ const capabilities = await this.getCapabilities();
1799
+ if (!capabilities.mcpSupport) {
1800
+ return {
1801
+ error: createErrorResponse('MCP not available', 'API_ERROR'),
1802
+ source: 'cli',
1803
+ mcpUsed: false
1804
+ };
1805
+ }
1806
+ try {
1807
+ const result = await this.cliIntegration.getMCPStatus();
1808
+ return { ...result, source: 'cli', mcpUsed: true };
1809
+ }
1810
+ catch (error) {
1811
+ return {
1812
+ error: createErrorResponse(error instanceof Error ? error.message : 'MCP status check failed', 'API_ERROR'),
1813
+ source: 'cli',
1814
+ mcpUsed: false
1815
+ };
1816
+ }
1817
+ }
1818
+ /**
1819
+ * Update authentication for both CLI and API client
1820
+ */
1821
+ setAuthToken(token) {
1822
+ this.directClient.setAuthToken(token);
1823
+ }
1824
+ setApiKey(apiKey) {
1825
+ this.directClient.setApiKey(apiKey);
1826
+ }
1827
+ clearAuth() {
1828
+ this.directClient.clearAuth();
1829
+ }
1830
+ /**
1831
+ * Update configuration
1832
+ */
1833
+ updateConfig(updates) {
1834
+ this.config = { ...this.config, ...updates };
1835
+ this.directClient.updateConfig(updates);
1836
+ }
1837
+ /**
1838
+ * Get configuration summary
1839
+ */
1840
+ getConfigSummary() {
1841
+ return {
1842
+ apiUrl: this.config.apiUrl,
1843
+ preferCLI: this.config.preferCLI,
1844
+ enableMCP: this.config.enableMCP,
1845
+ capabilities: this.capabilities || undefined
1846
+ };
1847
+ }
1848
+ }
1849
+ /**
1850
+ * Factory function to create an enhanced memory client
1851
+ */
1852
+ async function createNodeMemoryClient(config) {
1853
+ const client = new EnhancedMemoryClient(config);
1854
+ await client.initialize();
1855
+ return client;
1856
+ }
1857
+
1858
+ /**
1859
+ * @lanonasis/memory-client/node
1860
+ *
1861
+ * Node.js-enhanced client with CLI integration
1862
+ * ONLY import this in Node.js environments
1863
+ *
1864
+ * Provides intelligent CLI detection and MCP channel utilization
1865
+ * when @lanonasis/cli v1.5.2+ is available
1866
+ */
1867
+ // Enhanced client with CLI support
1868
+
1869
+ var index = /*#__PURE__*/Object.freeze({
1870
+ __proto__: null,
1871
+ CLIIntegration: CLIIntegration,
1872
+ EnhancedMemoryClient: EnhancedMemoryClient,
1873
+ createNodeMemoryClient: createNodeMemoryClient
1874
+ });
1875
+
1876
+ exports.ApiError = ApiError;
1877
+ exports.AuthenticationError = AuthenticationError;
1878
+ exports.CHUNKING_STRATEGIES = CHUNKING_STRATEGIES;
1879
+ exports.CLIENT_NAME = CLIENT_NAME;
1880
+ exports.CONTENT_TYPES = CONTENT_TYPES;
1881
+ exports.CoreMemoryClient = CoreMemoryClient;
1882
+ exports.ERROR_CODES = ERROR_CODES;
1883
+ exports.MEMORY_STATUSES = MEMORY_STATUSES;
1884
+ exports.MEMORY_TYPES = MEMORY_TYPES;
1885
+ exports.MemoryClient = CoreMemoryClient;
1886
+ exports.MemoryClientError = MemoryClientError;
1887
+ exports.NetworkError = NetworkError;
1888
+ exports.NotFoundError = NotFoundError;
1889
+ exports.RateLimitError = RateLimitError;
1890
+ exports.SEARCH_MODES = SEARCH_MODES;
1891
+ exports.ServerError = ServerError;
1892
+ exports.TimeoutError = TimeoutError;
1893
+ exports.VERSION = VERSION;
1894
+ exports.ValidationError = ValidationError;
1895
+ exports.analyticsDateRangeSchema = analyticsDateRangeSchema;
1896
+ exports.calculateRetryDelay = calculateRetryDelay;
1897
+ exports.createClient = createClient;
1898
+ exports.createErrorFromStatus = createErrorFromStatus;
1899
+ exports.createErrorResponse = createErrorResponse;
1900
+ exports.createMemoryClient = createMemoryClient;
1901
+ exports.createMemorySchema = createMemorySchema;
1902
+ exports.createTopicSchema = createTopicSchema;
1903
+ exports.defaultConfigs = defaultConfigs;
1904
+ exports.enhancedSearchSchema = enhancedSearchSchema;
1905
+ exports.getEnvironment = getEnvironment;
1906
+ exports.hasData = hasData;
1907
+ exports.hasError = hasError;
1908
+ exports.httpStatusToErrorCode = httpStatusToErrorCode;
1909
+ exports.isApiErrorResponse = isApiErrorResponse;
1910
+ exports.isBrowser = isBrowser;
1911
+ exports.isEdge = isEdge;
1912
+ exports.isNode = isNode;
1913
+ exports.isRetryableError = isRetryableError;
1914
+ exports.preprocessingOptionsSchema = preprocessingOptionsSchema;
1915
+ exports.safeJsonParse = safeJsonParse;
1916
+ exports.searchMemorySchema = searchMemorySchema;
1917
+ exports.updateMemorySchema = updateMemorySchema;
1918
+ //# sourceMappingURL=index.cjs.map