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