@lanonasis/memory-client 1.0.1 → 2.1.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,20 +1,259 @@
1
- import { exec, execSync } from 'child_process';
2
- import { promisify } from 'util';
3
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
+ // ========================================
55
+ // Intelligence Feature Types (v2.0)
56
+ // ========================================
57
+ /**
58
+ * Chunking strategies for content preprocessing
59
+ */
60
+ const CHUNKING_STRATEGIES = ['semantic', 'fixed-size', 'paragraph', 'sentence', 'code-block'];
61
+ /**
62
+ * Content types detected or specified
63
+ */
64
+ const CONTENT_TYPES = ['text', 'code', 'markdown', 'json', 'yaml'];
65
+ // ========================================
66
+ // Enhanced Search Types
67
+ // ========================================
68
+ /**
69
+ * Search modes for memory queries
70
+ */
71
+ const SEARCH_MODES = ['vector', 'text', 'hybrid'];
72
+ // ========================================
73
+ // Validation Schemas for Intelligence
74
+ // ========================================
75
+ const preprocessingOptionsSchema = z.object({
76
+ chunking: z.object({
77
+ strategy: z.enum(CHUNKING_STRATEGIES).optional(),
78
+ maxChunkSize: z.number().int().min(100).max(10000).optional(),
79
+ overlap: z.number().int().min(0).max(500).optional()
80
+ }).optional(),
81
+ cleanContent: z.boolean().optional(),
82
+ extractMetadata: z.boolean().optional()
83
+ }).optional();
84
+ const enhancedSearchSchema = z.object({
85
+ query: z.string().min(1).max(1000),
86
+ type: z.enum(MEMORY_TYPES).optional(),
87
+ threshold: z.number().min(0).max(1).default(0.7),
88
+ limit: z.number().int().min(1).max(100).default(20),
89
+ search_mode: z.enum(SEARCH_MODES).default('hybrid'),
90
+ filters: z.object({
91
+ tags: z.array(z.string()).optional(),
92
+ project_id: z.string().uuid().optional(),
93
+ topic_id: z.string().uuid().optional(),
94
+ date_range: z.object({
95
+ from: z.string().optional(),
96
+ to: z.string().optional()
97
+ }).optional()
98
+ }).optional(),
99
+ include_chunks: z.boolean().default(false)
100
+ });
101
+ const analyticsDateRangeSchema = z.object({
102
+ from: z.string().optional(),
103
+ to: z.string().optional(),
104
+ group_by: z.enum(['day', 'week', 'month']).default('day')
105
+ });
4
106
 
5
107
  /**
6
- * Memory Client class for interacting with the Memory as a Service API
108
+ * Core Utilities for Memory Client
109
+ * Browser-safe, no Node.js dependencies
110
+ */
111
+ /**
112
+ * Safely parse JSON with detailed error reporting
113
+ * Prevents scattered try/catch blocks throughout the codebase
114
+ */
115
+ function safeJsonParse(input) {
116
+ try {
117
+ const data = JSON.parse(input);
118
+ return { success: true, data };
119
+ }
120
+ catch (error) {
121
+ const message = error instanceof Error
122
+ ? error.message
123
+ : 'Unknown JSON parse error';
124
+ return { success: false, error: `Invalid JSON: ${message}` };
125
+ }
126
+ }
127
+ /**
128
+ * HTTP status code to error code mapping
7
129
  */
8
- class MemoryClient {
130
+ function httpStatusToErrorCode(status) {
131
+ switch (status) {
132
+ case 400:
133
+ return 'VALIDATION_ERROR';
134
+ case 401:
135
+ return 'AUTH_ERROR';
136
+ case 403:
137
+ return 'FORBIDDEN';
138
+ case 404:
139
+ return 'NOT_FOUND';
140
+ case 408:
141
+ return 'TIMEOUT_ERROR';
142
+ case 409:
143
+ return 'CONFLICT';
144
+ case 429:
145
+ return 'RATE_LIMIT_ERROR';
146
+ case 500:
147
+ case 502:
148
+ case 503:
149
+ case 504:
150
+ return 'SERVER_ERROR';
151
+ default:
152
+ return 'API_ERROR';
153
+ }
154
+ }
155
+ /**
156
+ * Create a standardized error response from various error sources
157
+ */
158
+ function createErrorResponse(message, code = 'API_ERROR', statusCode, details) {
159
+ return {
160
+ code,
161
+ message,
162
+ statusCode,
163
+ details,
164
+ timestamp: new Date().toISOString()
165
+ };
166
+ }
167
+ /**
168
+ * Create an error response from an HTTP response
169
+ */
170
+ function createErrorFromResponse(status, statusText, body) {
171
+ const code = httpStatusToErrorCode(status);
172
+ // Try to extract message from response body
173
+ let message = `HTTP ${status}: ${statusText}`;
174
+ let details = undefined;
175
+ if (body && typeof body === 'object') {
176
+ const bodyObj = body;
177
+ if (typeof bodyObj.error === 'string') {
178
+ message = bodyObj.error;
179
+ }
180
+ else if (typeof bodyObj.message === 'string') {
181
+ message = bodyObj.message;
182
+ }
183
+ if (bodyObj.details) {
184
+ details = bodyObj.details;
185
+ }
186
+ }
187
+ return createErrorResponse(message, code, status, details);
188
+ }
189
+ /**
190
+ * Sleep utility for retry logic
191
+ */
192
+ function sleep(ms) {
193
+ return new Promise(resolve => setTimeout(resolve, ms));
194
+ }
195
+ /**
196
+ * Calculate retry delay with exponential backoff and jitter
197
+ */
198
+ function calculateRetryDelay(attempt, baseDelay = 1000, backoff = 'exponential', maxDelay = 30000) {
199
+ let delay;
200
+ if (backoff === 'exponential') {
201
+ delay = baseDelay * Math.pow(2, attempt);
202
+ }
203
+ else {
204
+ delay = baseDelay * (attempt + 1);
205
+ }
206
+ // Add jitter (±20%) to prevent thundering herd
207
+ const jitter = delay * 0.2 * (Math.random() * 2 - 1);
208
+ delay = Math.min(delay + jitter, maxDelay);
209
+ return Math.round(delay);
210
+ }
211
+ /**
212
+ * Check if an error is retryable based on status code
213
+ */
214
+ function isRetryableError(statusCode) {
215
+ if (!statusCode)
216
+ return true; // Network errors are retryable
217
+ // Retry on server errors and rate limits
218
+ return statusCode >= 500 || statusCode === 429 || statusCode === 408;
219
+ }
220
+
221
+ /**
222
+ * Core Memory Client - Pure Browser-Safe Implementation
223
+ *
224
+ * NO Node.js dependencies, NO CLI code, NO child_process
225
+ * Works in: Browser, React Native, Cloudflare Workers, Edge Functions, Deno, Bun
226
+ *
227
+ * Bundle size: ~15KB gzipped
228
+ */
229
+ /**
230
+ * Helper to check if response has error
231
+ */
232
+ function hasError(response) {
233
+ return response.error !== undefined;
234
+ }
235
+ /**
236
+ * Helper to check if response has data
237
+ */
238
+ function hasData(response) {
239
+ return response.data !== undefined;
240
+ }
241
+ /**
242
+ * Core Memory Client class for interacting with the Memory as a Service API
243
+ *
244
+ * This is a pure browser-safe client with zero Node.js dependencies.
245
+ * It uses only standard web APIs (fetch, AbortController, etc.)
246
+ */
247
+ class CoreMemoryClient {
9
248
  constructor(config) {
10
249
  this.config = {
11
250
  timeout: 30000,
12
- useGateway: true,
13
251
  ...config
14
252
  };
15
253
  this.baseHeaders = {
16
254
  'Content-Type': 'application/json',
17
- 'User-Agent': '@lanonasis/memory-client/1.0.0',
255
+ 'User-Agent': '@lanonasis/memory-client/2.0.0',
256
+ 'X-Project-Scope': 'lanonasis-maas', // Required by backend auth middleware
18
257
  ...config.headers
19
258
  };
20
259
  // Set authentication headers
@@ -24,48 +263,171 @@ class MemoryClient {
24
263
  else if (config.apiKey) {
25
264
  this.baseHeaders['X-API-Key'] = config.apiKey;
26
265
  }
266
+ // Add organization ID header if provided
267
+ if (config.organizationId) {
268
+ this.baseHeaders['X-Organization-ID'] = config.organizationId;
269
+ }
27
270
  }
28
271
  /**
29
- * Make an HTTP request to the API
272
+ * Enrich request body with organization context if configured
273
+ * This ensures the API has the organization_id even if not in auth token
274
+ */
275
+ enrichWithOrgContext(body) {
276
+ // If organizationId is configured, include it in the request body
277
+ if (this.config.organizationId && !body.organization_id) {
278
+ return {
279
+ ...body,
280
+ organization_id: this.config.organizationId
281
+ };
282
+ }
283
+ // Fallback to userId if no organizationId configured
284
+ if (!this.config.organizationId && this.config.userId && !body.organization_id) {
285
+ return {
286
+ ...body,
287
+ organization_id: this.config.userId
288
+ };
289
+ }
290
+ return body;
291
+ }
292
+ /**
293
+ * Make an HTTP request to the API with retry support
30
294
  */
31
295
  async request(endpoint, options = {}) {
296
+ const startTime = Date.now();
297
+ const maxRetries = this.config.retry?.maxRetries ?? 3;
298
+ const baseDelay = this.config.retry?.retryDelay ?? 1000;
299
+ const backoff = this.config.retry?.backoff ?? 'exponential';
300
+ // Call onRequest hook if provided
301
+ if (this.config.onRequest) {
302
+ try {
303
+ this.config.onRequest(endpoint);
304
+ }
305
+ catch (error) {
306
+ console.warn('onRequest hook error:', error);
307
+ }
308
+ }
32
309
  // Handle gateway vs direct API URL formatting
33
310
  const baseUrl = this.config.apiUrl.includes('/api')
34
311
  ? this.config.apiUrl.replace('/api', '')
35
312
  : this.config.apiUrl;
36
313
  const url = `${baseUrl}/api/v1${endpoint}`;
37
- try {
38
- const controller = new AbortController();
39
- const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
40
- const response = await fetch(url, {
41
- headers: { ...this.baseHeaders, ...options.headers },
42
- signal: controller.signal,
43
- ...options,
44
- });
45
- clearTimeout(timeoutId);
46
- let data;
47
- const contentType = response.headers.get('content-type');
48
- if (contentType && contentType.includes('application/json')) {
49
- data = await response.json();
50
- }
51
- else {
52
- data = await response.text();
314
+ let lastError;
315
+ let attempt = 0;
316
+ while (attempt <= maxRetries) {
317
+ try {
318
+ const controller = new AbortController();
319
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
320
+ const response = await fetch(url, {
321
+ headers: { ...this.baseHeaders, ...options.headers },
322
+ signal: controller.signal,
323
+ ...options,
324
+ });
325
+ clearTimeout(timeoutId);
326
+ let data;
327
+ const contentType = response.headers.get('content-type');
328
+ if (contentType && contentType.includes('application/json')) {
329
+ data = await response.json();
330
+ }
331
+ else {
332
+ data = await response.text();
333
+ }
334
+ if (!response.ok) {
335
+ const error = createErrorFromResponse(response.status, response.statusText, data);
336
+ // Only retry on retryable errors (5xx, 429, 408)
337
+ if (isRetryableError(response.status) && attempt < maxRetries) {
338
+ lastError = error;
339
+ const delay = calculateRetryDelay(attempt, baseDelay, backoff);
340
+ await sleep(delay);
341
+ attempt++;
342
+ continue;
343
+ }
344
+ // Call onError hook if provided
345
+ if (this.config.onError) {
346
+ try {
347
+ this.config.onError(error);
348
+ }
349
+ catch (hookError) {
350
+ console.warn('onError hook error:', hookError);
351
+ }
352
+ }
353
+ return { error, meta: { duration: Date.now() - startTime, retries: attempt } };
354
+ }
355
+ // Call onResponse hook if provided
356
+ if (this.config.onResponse) {
357
+ try {
358
+ const duration = Date.now() - startTime;
359
+ this.config.onResponse(endpoint, duration);
360
+ }
361
+ catch (error) {
362
+ console.warn('onResponse hook error:', error);
363
+ }
364
+ }
365
+ return { data, meta: { duration: Date.now() - startTime, retries: attempt } };
53
366
  }
54
- if (!response.ok) {
55
- return {
56
- error: data?.error || `HTTP ${response.status}: ${response.statusText}`
57
- };
367
+ catch (error) {
368
+ if (error instanceof Error && error.name === 'AbortError') {
369
+ const timeoutError = createErrorResponse('Request timeout', 'TIMEOUT_ERROR', 408);
370
+ // Retry on timeout
371
+ if (attempt < maxRetries) {
372
+ lastError = timeoutError;
373
+ const delay = calculateRetryDelay(attempt, baseDelay, backoff);
374
+ await sleep(delay);
375
+ attempt++;
376
+ continue;
377
+ }
378
+ if (this.config.onError) {
379
+ try {
380
+ this.config.onError(timeoutError);
381
+ }
382
+ catch (hookError) {
383
+ console.warn('onError hook error:', hookError);
384
+ }
385
+ }
386
+ return { error: timeoutError, meta: { duration: Date.now() - startTime, retries: attempt } };
387
+ }
388
+ const networkError = createErrorResponse(error instanceof Error ? error.message : 'Network error', 'NETWORK_ERROR');
389
+ // Retry on network errors
390
+ if (attempt < maxRetries) {
391
+ lastError = networkError;
392
+ const delay = calculateRetryDelay(attempt, baseDelay, backoff);
393
+ await sleep(delay);
394
+ attempt++;
395
+ continue;
396
+ }
397
+ if (this.config.onError) {
398
+ try {
399
+ this.config.onError(networkError);
400
+ }
401
+ catch (hookError) {
402
+ console.warn('onError hook error:', hookError);
403
+ }
404
+ }
405
+ return { error: networkError, meta: { duration: Date.now() - startTime, retries: attempt } };
58
406
  }
59
- return { data };
60
407
  }
61
- catch (error) {
62
- if (error instanceof Error && error.name === 'AbortError') {
63
- return { error: 'Request timeout' };
64
- }
408
+ // Should never reach here, but handle it gracefully
409
+ return {
410
+ error: lastError ?? createErrorResponse('Max retries exceeded', 'API_ERROR'),
411
+ meta: { duration: Date.now() - startTime, retries: attempt }
412
+ };
413
+ }
414
+ /**
415
+ * Validate input using Zod schema and return validation error if invalid
416
+ */
417
+ validateInput(schema, data) {
418
+ const result = schema.safeParse(data);
419
+ if (!result.success) {
420
+ // Extract error details from Zod error
421
+ const zodError = result.error;
422
+ const details = zodError?.issues?.map(issue => ({
423
+ field: issue.path.map(String).join('.'),
424
+ message: issue.message
425
+ })) ?? [];
65
426
  return {
66
- error: error instanceof Error ? error.message : 'Network error'
427
+ error: createErrorResponse('Validation failed', 'VALIDATION_ERROR', 400, details)
67
428
  };
68
429
  }
430
+ return null;
69
431
  }
70
432
  /**
71
433
  * Test the API connection and authentication
@@ -75,12 +437,18 @@ class MemoryClient {
75
437
  }
76
438
  // Memory Operations
77
439
  /**
78
- * Create a new memory
440
+ * Create a new memory with validation
79
441
  */
80
442
  async createMemory(memory) {
443
+ // Validate input before making request
444
+ const validationError = this.validateInput(createMemorySchema, memory);
445
+ if (validationError) {
446
+ return { error: validationError.error };
447
+ }
448
+ const enrichedMemory = this.enrichWithOrgContext(memory);
81
449
  return this.request('/memory', {
82
450
  method: 'POST',
83
- body: JSON.stringify(memory)
451
+ body: JSON.stringify(enrichedMemory)
84
452
  });
85
453
  }
86
454
  /**
@@ -90,9 +458,14 @@ class MemoryClient {
90
458
  return this.request(`/memory/${encodeURIComponent(id)}`);
91
459
  }
92
460
  /**
93
- * Update an existing memory
461
+ * Update an existing memory with validation
94
462
  */
95
463
  async updateMemory(id, updates) {
464
+ // Validate input before making request
465
+ const validationError = this.validateInput(updateMemorySchema, updates);
466
+ if (validationError) {
467
+ return { error: validationError.error };
468
+ }
96
469
  return this.request(`/memory/${encodeURIComponent(id)}`, {
97
470
  method: 'PUT',
98
471
  body: JSON.stringify(updates)
@@ -126,31 +499,45 @@ class MemoryClient {
126
499
  return this.request(endpoint);
127
500
  }
128
501
  /**
129
- * Search memories using semantic search
502
+ * Search memories using semantic search with validation
130
503
  */
131
504
  async searchMemories(request) {
505
+ // Validate input before making request
506
+ const validationError = this.validateInput(searchMemorySchema, request);
507
+ if (validationError) {
508
+ // Return error response (data will be undefined, only error is set)
509
+ return { error: validationError.error };
510
+ }
511
+ const enrichedRequest = this.enrichWithOrgContext(request);
132
512
  return this.request('/memory/search', {
133
513
  method: 'POST',
134
- body: JSON.stringify(request)
514
+ body: JSON.stringify(enrichedRequest)
135
515
  });
136
516
  }
137
517
  /**
138
518
  * Bulk delete multiple memories
139
519
  */
140
520
  async bulkDeleteMemories(memoryIds) {
521
+ const enrichedRequest = this.enrichWithOrgContext({ memory_ids: memoryIds });
141
522
  return this.request('/memory/bulk/delete', {
142
523
  method: 'POST',
143
- body: JSON.stringify({ memory_ids: memoryIds })
524
+ body: JSON.stringify(enrichedRequest)
144
525
  });
145
526
  }
146
527
  // Topic Operations
147
528
  /**
148
- * Create a new topic
529
+ * Create a new topic with validation
149
530
  */
150
531
  async createTopic(topic) {
532
+ // Validate input before making request
533
+ const validationError = this.validateInput(createTopicSchema, topic);
534
+ if (validationError) {
535
+ return { error: validationError.error };
536
+ }
537
+ const enrichedTopic = this.enrichWithOrgContext(topic);
151
538
  return this.request('/topics', {
152
539
  method: 'POST',
153
- body: JSON.stringify(topic)
540
+ body: JSON.stringify(enrichedTopic)
154
541
  });
155
542
  }
156
543
  /**
@@ -188,6 +575,182 @@ class MemoryClient {
188
575
  async getMemoryStats() {
189
576
  return this.request('/memory/stats');
190
577
  }
578
+ // ========================================
579
+ // Intelligence Features (v2.0)
580
+ // ========================================
581
+ /**
582
+ * Create a memory with preprocessing options (chunking, intelligence extraction)
583
+ *
584
+ * @example
585
+ * ```typescript
586
+ * const result = await client.createMemoryWithPreprocessing({
587
+ * title: 'Auth System Docs',
588
+ * content: 'Long content...',
589
+ * memory_type: 'knowledge',
590
+ * preprocessing: {
591
+ * chunking: { strategy: 'semantic', maxChunkSize: 1000 },
592
+ * extractMetadata: true
593
+ * }
594
+ * });
595
+ * ```
596
+ */
597
+ async createMemoryWithPreprocessing(memory) {
598
+ // Validate base memory fields
599
+ const validationError = this.validateInput(createMemorySchema, memory);
600
+ if (validationError) {
601
+ return { error: validationError.error };
602
+ }
603
+ const enrichedMemory = this.enrichWithOrgContext(memory);
604
+ return this.request('/memory', {
605
+ method: 'POST',
606
+ body: JSON.stringify(enrichedMemory)
607
+ });
608
+ }
609
+ /**
610
+ * Update a memory with re-chunking and embedding regeneration
611
+ *
612
+ * @example
613
+ * ```typescript
614
+ * const result = await client.updateMemoryWithPreprocessing('mem_123', {
615
+ * content: 'Updated content...',
616
+ * rechunk: true,
617
+ * regenerate_embedding: true
618
+ * });
619
+ * ```
620
+ */
621
+ async updateMemoryWithPreprocessing(id, updates) {
622
+ const validationError = this.validateInput(updateMemorySchema, updates);
623
+ if (validationError) {
624
+ return { error: validationError.error };
625
+ }
626
+ return this.request(`/memory/${encodeURIComponent(id)}`, {
627
+ method: 'PUT',
628
+ body: JSON.stringify(updates)
629
+ });
630
+ }
631
+ /**
632
+ * Enhanced semantic search with hybrid mode (vector + text)
633
+ *
634
+ * @example
635
+ * ```typescript
636
+ * const result = await client.enhancedSearch({
637
+ * query: 'authentication flow',
638
+ * search_mode: 'hybrid',
639
+ * filters: { tags: ['auth'], project_id: 'proj_123' },
640
+ * include_chunks: true
641
+ * });
642
+ * ```
643
+ */
644
+ async enhancedSearch(request) {
645
+ const validationError = this.validateInput(enhancedSearchSchema, request);
646
+ if (validationError) {
647
+ return { error: validationError.error };
648
+ }
649
+ const enrichedRequest = this.enrichWithOrgContext(request);
650
+ return this.request('/memory/search', {
651
+ method: 'POST',
652
+ body: JSON.stringify(enrichedRequest)
653
+ });
654
+ }
655
+ // ========================================
656
+ // Analytics Operations
657
+ // ========================================
658
+ /**
659
+ * Get search analytics data
660
+ *
661
+ * @example
662
+ * ```typescript
663
+ * const analytics = await client.getSearchAnalytics({
664
+ * from: '2025-01-01',
665
+ * to: '2025-12-31',
666
+ * group_by: 'day'
667
+ * });
668
+ * ```
669
+ */
670
+ async getSearchAnalytics(options = {}) {
671
+ const validationError = this.validateInput(analyticsDateRangeSchema, options);
672
+ if (validationError) {
673
+ return { error: validationError.error };
674
+ }
675
+ const params = new URLSearchParams();
676
+ if (options.from)
677
+ params.append('from', options.from);
678
+ if (options.to)
679
+ params.append('to', options.to);
680
+ if (options.group_by)
681
+ params.append('group_by', options.group_by);
682
+ const queryString = params.toString();
683
+ const endpoint = queryString ? `/analytics/search?${queryString}` : '/analytics/search';
684
+ return this.request(endpoint);
685
+ }
686
+ /**
687
+ * Get memory access patterns
688
+ *
689
+ * @example
690
+ * ```typescript
691
+ * const patterns = await client.getAccessPatterns({
692
+ * from: '2025-01-01',
693
+ * to: '2025-12-31'
694
+ * });
695
+ * console.log(patterns.data?.most_accessed);
696
+ * ```
697
+ */
698
+ async getAccessPatterns(options = {}) {
699
+ const params = new URLSearchParams();
700
+ if (options.from)
701
+ params.append('from', options.from);
702
+ if (options.to)
703
+ params.append('to', options.to);
704
+ const queryString = params.toString();
705
+ const endpoint = queryString ? `/analytics/access?${queryString}` : '/analytics/access';
706
+ return this.request(endpoint);
707
+ }
708
+ /**
709
+ * Get extended memory statistics with storage and activity metrics
710
+ *
711
+ * @example
712
+ * ```typescript
713
+ * const stats = await client.getExtendedStats();
714
+ * console.log(`Total chunks: ${stats.data?.storage.total_chunks}`);
715
+ * console.log(`Created today: ${stats.data?.activity.created_today}`);
716
+ * ```
717
+ */
718
+ async getExtendedStats() {
719
+ return this.request('/analytics/stats');
720
+ }
721
+ /**
722
+ * Get topic with its memories
723
+ *
724
+ * @example
725
+ * ```typescript
726
+ * const topic = await client.getTopicWithMemories('topic_123');
727
+ * console.log(topic.data?.memories);
728
+ * ```
729
+ */
730
+ async getTopicWithMemories(topicId, options = {}) {
731
+ const params = new URLSearchParams();
732
+ if (options.limit)
733
+ params.append('limit', String(options.limit));
734
+ if (options.offset)
735
+ params.append('offset', String(options.offset));
736
+ const queryString = params.toString();
737
+ const endpoint = queryString
738
+ ? `/topics/${encodeURIComponent(topicId)}/memories?${queryString}`
739
+ : `/topics/${encodeURIComponent(topicId)}/memories`;
740
+ return this.request(endpoint);
741
+ }
742
+ /**
743
+ * Get topics in hierarchical structure
744
+ *
745
+ * @example
746
+ * ```typescript
747
+ * const topics = await client.getTopicsHierarchy();
748
+ * // Returns nested topic tree with children
749
+ * ```
750
+ */
751
+ async getTopicsHierarchy() {
752
+ return this.request('/topics?include_hierarchy=true');
753
+ }
191
754
  // Utility Methods
192
755
  /**
193
756
  * Update authentication token
@@ -229,17 +792,431 @@ class MemoryClient {
229
792
  }
230
793
  }
231
794
  /**
232
- * Factory function to create a new Memory Client instance
795
+ * Factory function to create a new Core Memory Client instance
233
796
  */
234
797
  function createMemoryClient(config) {
235
- return new MemoryClient(config);
798
+ return new CoreMemoryClient(config);
236
799
  }
237
800
 
801
+ /**
802
+ * Error handling for Memory Client
803
+ * Browser-safe, no Node.js dependencies
804
+ */
805
+ /**
806
+ * Standardized error codes for programmatic error handling
807
+ */
808
+ const ERROR_CODES = [
809
+ 'API_ERROR',
810
+ 'AUTH_ERROR',
811
+ 'VALIDATION_ERROR',
812
+ 'TIMEOUT_ERROR',
813
+ 'RATE_LIMIT_ERROR',
814
+ 'NOT_FOUND',
815
+ 'NETWORK_ERROR',
816
+ 'FORBIDDEN',
817
+ 'CONFLICT',
818
+ 'SERVER_ERROR'
819
+ ];
820
+ /**
821
+ * Type guard to check if an object is an ApiErrorResponse
822
+ */
823
+ function isApiErrorResponse(value) {
824
+ return (typeof value === 'object' &&
825
+ value !== null &&
826
+ 'code' in value &&
827
+ 'message' in value &&
828
+ typeof value.code === 'string' &&
829
+ typeof value.message === 'string');
830
+ }
831
+ /**
832
+ * Base error class for Memory Client errors
833
+ */
834
+ class MemoryClientError extends Error {
835
+ constructor(message, code = 'API_ERROR', statusCode, details) {
836
+ super(message);
837
+ this.code = code;
838
+ this.statusCode = statusCode;
839
+ this.details = details;
840
+ this.name = 'MemoryClientError';
841
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
842
+ if (Error.captureStackTrace) {
843
+ Error.captureStackTrace(this, MemoryClientError);
844
+ }
845
+ }
846
+ /**
847
+ * Convert to ApiErrorResponse for consistent API responses
848
+ */
849
+ toResponse() {
850
+ return {
851
+ code: this.code,
852
+ message: this.message,
853
+ statusCode: this.statusCode,
854
+ details: this.details,
855
+ timestamp: new Date().toISOString()
856
+ };
857
+ }
858
+ }
859
+ /**
860
+ * Network/API error
861
+ */
862
+ class ApiError extends MemoryClientError {
863
+ constructor(message, statusCode, details) {
864
+ super(message, 'API_ERROR', statusCode, details);
865
+ this.name = 'ApiError';
866
+ }
867
+ /**
868
+ * Create from an HTTP response
869
+ */
870
+ static fromResponse(status, statusText, body) {
871
+ let message = `HTTP ${status}: ${statusText}`;
872
+ let details = undefined;
873
+ if (body && typeof body === 'object') {
874
+ const bodyObj = body;
875
+ if (typeof bodyObj.error === 'string') {
876
+ message = bodyObj.error;
877
+ }
878
+ else if (typeof bodyObj.message === 'string') {
879
+ message = bodyObj.message;
880
+ }
881
+ if (bodyObj.details) {
882
+ details = bodyObj.details;
883
+ }
884
+ }
885
+ return new ApiError(message, status, details);
886
+ }
887
+ }
888
+ /**
889
+ * Authentication error
890
+ */
891
+ class AuthenticationError extends MemoryClientError {
892
+ constructor(message = 'Authentication required') {
893
+ super(message, 'AUTH_ERROR', 401);
894
+ this.name = 'AuthenticationError';
895
+ }
896
+ }
897
+ /**
898
+ * Validation error with field-level details
899
+ */
900
+ class ValidationError extends MemoryClientError {
901
+ constructor(message, details) {
902
+ super(message, 'VALIDATION_ERROR', 400, details);
903
+ this.name = 'ValidationError';
904
+ // Parse validation details into field errors
905
+ this.validationErrors = [];
906
+ if (Array.isArray(details)) {
907
+ this.validationErrors = details.filter((item) => typeof item === 'object' &&
908
+ item !== null &&
909
+ typeof item.field === 'string' &&
910
+ typeof item.message === 'string');
911
+ }
912
+ }
913
+ /**
914
+ * Create from Zod error
915
+ */
916
+ static fromZodError(error) {
917
+ const details = error.issues.map(issue => ({
918
+ field: issue.path.join('.'),
919
+ message: issue.message
920
+ }));
921
+ return new ValidationError('Validation failed', details);
922
+ }
923
+ }
924
+ /**
925
+ * Timeout error
926
+ */
927
+ class TimeoutError extends MemoryClientError {
928
+ constructor(message = 'Request timeout') {
929
+ super(message, 'TIMEOUT_ERROR', 408);
930
+ this.name = 'TimeoutError';
931
+ }
932
+ }
933
+ /**
934
+ * Rate limit error with retry-after info
935
+ */
936
+ class RateLimitError extends MemoryClientError {
937
+ constructor(message = 'Rate limit exceeded', retryAfter) {
938
+ super(message, 'RATE_LIMIT_ERROR', 429, { retryAfter });
939
+ this.name = 'RateLimitError';
940
+ this.retryAfter = retryAfter;
941
+ }
942
+ }
943
+ /**
944
+ * Not found error
945
+ */
946
+ class NotFoundError extends MemoryClientError {
947
+ constructor(resource) {
948
+ super(`${resource} not found`, 'NOT_FOUND', 404);
949
+ this.name = 'NotFoundError';
950
+ this.resource = resource;
951
+ }
952
+ }
953
+ /**
954
+ * Network error (no response received)
955
+ */
956
+ class NetworkError extends MemoryClientError {
957
+ constructor(message = 'Network error') {
958
+ super(message, 'NETWORK_ERROR');
959
+ this.name = 'NetworkError';
960
+ }
961
+ }
962
+ /**
963
+ * Server error (5xx responses)
964
+ */
965
+ class ServerError extends MemoryClientError {
966
+ constructor(message, statusCode = 500) {
967
+ super(message, 'SERVER_ERROR', statusCode);
968
+ this.name = 'ServerError';
969
+ }
970
+ }
971
+ /**
972
+ * Create appropriate error class from status code
973
+ */
974
+ function createErrorFromStatus(status, message, details) {
975
+ switch (status) {
976
+ case 400:
977
+ return new ValidationError(message, details);
978
+ case 401:
979
+ return new AuthenticationError(message);
980
+ case 404:
981
+ return new NotFoundError(message);
982
+ case 408:
983
+ return new TimeoutError(message);
984
+ case 429:
985
+ return new RateLimitError(message);
986
+ case 500:
987
+ case 502:
988
+ case 503:
989
+ case 504:
990
+ return new ServerError(message, status);
991
+ default:
992
+ return new ApiError(message, status, details);
993
+ }
994
+ }
995
+
996
+ /**
997
+ * @lanonasis/memory-client
998
+ *
999
+ * Universal Memory as a Service (MaaS) Client SDK for Lanonasis
1000
+ * Intelligent memory management with semantic search capabilities
1001
+ *
1002
+ * v2.0.0 - Universal SDK Redesign
1003
+ * "Drop In and Sleep" Architecture - Works everywhere with zero configuration
1004
+ *
1005
+ * @example Browser/Web App
1006
+ * ```ts
1007
+ * import { createMemoryClient } from '@lanonasis/memory-client/core';
1008
+ * const client = createMemoryClient({ apiKey: 'your-key' });
1009
+ * ```
1010
+ *
1011
+ * @example Node.js
1012
+ * ```ts
1013
+ * import { createNodeMemoryClient } from '@lanonasis/memory-client/node';
1014
+ * const client = await createNodeMemoryClient({ apiKey: process.env.KEY });
1015
+ * ```
1016
+ *
1017
+ * @example React
1018
+ * ```tsx
1019
+ * import { MemoryProvider, useMemories } from '@lanonasis/memory-client/react';
1020
+ * ```
1021
+ *
1022
+ * @example Vue
1023
+ * ```ts
1024
+ * import { createMemoryPlugin, useMemories } from '@lanonasis/memory-client/vue';
1025
+ * ```
1026
+ */
1027
+ // ========================================
1028
+ // Internal Imports (for use in this file)
1029
+ // ========================================
1030
+ // ========================================
1031
+ // Constants
1032
+ // ========================================
1033
+ const VERSION = '2.0.0';
1034
+ const CLIENT_NAME = '@lanonasis/memory-client';
1035
+ // ========================================
1036
+ // Environment Detection
1037
+ // ========================================
1038
+ const isBrowser = typeof window !== 'undefined';
1039
+ const isNode = typeof globalThis !== 'undefined' && 'process' in globalThis && globalThis.process?.versions?.node;
1040
+ const isEdge = !isBrowser && !isNode;
1041
+ /**
1042
+ * Get the current runtime environment
1043
+ */
1044
+ function getEnvironment() {
1045
+ if (isBrowser)
1046
+ return 'browser';
1047
+ if (isNode)
1048
+ return 'node';
1049
+ return 'edge';
1050
+ }
1051
+ /**
1052
+ * Auto-detecting client factory - "Drop In and Sleep" architecture
1053
+ *
1054
+ * Automatically detects the runtime environment and returns the appropriate client:
1055
+ * - Browser/Edge: Returns CoreMemoryClient (lightweight, browser-safe)
1056
+ * - Node.js: Returns EnhancedMemoryClient (with CLI/MCP support)
1057
+ *
1058
+ * @example
1059
+ * ```typescript
1060
+ * import { createClient } from '@lanonasis/memory-client';
1061
+ *
1062
+ * // Works in any environment!
1063
+ * const client = await createClient({
1064
+ * apiUrl: 'https://api.lanonasis.com',
1065
+ * apiKey: 'your-key'
1066
+ * });
1067
+ *
1068
+ * const memories = await client.listMemories();
1069
+ * ```
1070
+ */
1071
+ async function createClient(config) {
1072
+ const environment = getEnvironment();
1073
+ if (environment === 'node') {
1074
+ try {
1075
+ // Dynamic import for Node.js client to avoid bundling in browser
1076
+ const { createNodeMemoryClient } = await Promise.resolve().then(function () { return index; });
1077
+ return await createNodeMemoryClient({
1078
+ ...config,
1079
+ preferCLI: config.preferCLI ?? true,
1080
+ enableMCP: config.enableMCP ?? true
1081
+ });
1082
+ }
1083
+ catch {
1084
+ // Fallback to core client if Node module fails to load
1085
+ console.warn('Failed to load Node.js client, falling back to core client');
1086
+ }
1087
+ }
1088
+ // Browser, Edge, or fallback
1089
+ const clientConfig = {
1090
+ apiUrl: config.apiUrl,
1091
+ apiKey: config.apiKey,
1092
+ authToken: config.authToken,
1093
+ organizationId: config.organizationId,
1094
+ timeout: config.timeout ?? (environment === 'edge' ? 5000 : 30000),
1095
+ headers: config.headers,
1096
+ retry: config.retry
1097
+ };
1098
+ return createMemoryClient(clientConfig);
1099
+ }
1100
+ // ========================================
1101
+ // Default Configurations
1102
+ // ========================================
1103
+ const defaultConfigs = {
1104
+ development: {
1105
+ apiUrl: 'http://localhost:3001',
1106
+ timeout: 30000,
1107
+ },
1108
+ production: {
1109
+ apiUrl: 'https://api.lanonasis.com',
1110
+ timeout: 15000,
1111
+ },
1112
+ edge: {
1113
+ apiUrl: 'https://api.lanonasis.com',
1114
+ timeout: 5000,
1115
+ }
1116
+ };
1117
+ // Note: Enhanced client requires Node.js, so we don't export it from main entry
1118
+ // Users should import from '@lanonasis/memory-client/node' instead
1119
+ // ========================================
1120
+ // Usage Instructions
1121
+ // ========================================
1122
+ /**
1123
+ * # @lanonasis/memory-client v2.0
1124
+ *
1125
+ * ## Quick Start
1126
+ *
1127
+ * ### Browser / Web App
1128
+ * ```bash
1129
+ * npm install @lanonasis/memory-client
1130
+ * ```
1131
+ * ```typescript
1132
+ * import { createMemoryClient } from '@lanonasis/memory-client/core';
1133
+ *
1134
+ * const client = createMemoryClient({
1135
+ * apiUrl: 'https://api.lanonasis.com',
1136
+ * apiKey: 'your-key-here'
1137
+ * });
1138
+ *
1139
+ * const memories = await client.listMemories();
1140
+ * ```
1141
+ *
1142
+ * ### Node.js with CLI Support
1143
+ * ```typescript
1144
+ * import { createNodeMemoryClient } from '@lanonasis/memory-client/node';
1145
+ *
1146
+ * const client = await createNodeMemoryClient({
1147
+ * apiKey: process.env.LANONASIS_KEY,
1148
+ * preferCLI: true // Automatically uses CLI if available
1149
+ * });
1150
+ *
1151
+ * const result = await client.listMemories();
1152
+ * console.log(`Using: ${result.source}`); // 'cli' or 'api'
1153
+ * ```
1154
+ *
1155
+ * ### React
1156
+ * ```tsx
1157
+ * import { MemoryProvider, useMemories } from '@lanonasis/memory-client/react';
1158
+ *
1159
+ * function App() {
1160
+ * return (
1161
+ * <MemoryProvider apiKey="your-key">
1162
+ * <MemoryList />
1163
+ * </MemoryProvider>
1164
+ * );
1165
+ * }
1166
+ *
1167
+ * function MemoryList() {
1168
+ * const { memories, loading } = useMemories();
1169
+ * if (loading) return <div>Loading...</div>;
1170
+ * return <div>{memories.map(m => <div key={m.id}>{m.title}</div>)}</div>;
1171
+ * }
1172
+ * ```
1173
+ *
1174
+ * ### Vue 3
1175
+ * ```typescript
1176
+ * import { createMemoryPlugin, useMemories } from '@lanonasis/memory-client/vue';
1177
+ *
1178
+ * const app = createApp(App);
1179
+ * app.use(createMemoryPlugin({ apiKey: 'your-key' }));
1180
+ * ```
1181
+ *
1182
+ * ### Edge Functions (Cloudflare Workers, Vercel Edge)
1183
+ * ```typescript
1184
+ * import { createMemoryClient } from '@lanonasis/memory-client/core';
1185
+ * import { edgePreset } from '@lanonasis/memory-client/presets';
1186
+ *
1187
+ * export default {
1188
+ * async fetch(request: Request, env: Env) {
1189
+ * const client = createMemoryClient(edgePreset({
1190
+ * apiKey: env.LANONASIS_KEY
1191
+ * }));
1192
+ * const memories = await client.searchMemories({ query: 'test' });
1193
+ * return Response.json(memories.data);
1194
+ * }
1195
+ * };
1196
+ * ```
1197
+ *
1198
+ * ## Bundle Sizes
1199
+ *
1200
+ * - **Core** (browser): ~15KB gzipped
1201
+ * - **Node** (with CLI): ~35KB gzipped
1202
+ * - **React**: ~18KB gzipped (+ React)
1203
+ * - **Vue**: ~17KB gzipped (+ Vue)
1204
+ * - **Presets**: ~2KB gzipped
1205
+ *
1206
+ * ## Documentation
1207
+ *
1208
+ * - Full docs: https://docs.lanonasis.com/sdk
1209
+ * - API reference: https://docs.lanonasis.com/api
1210
+ * - Examples: https://github.com/lanonasis/examples
1211
+ */
1212
+
238
1213
  /**
239
1214
  * CLI Integration Module for Memory Client SDK
240
1215
  *
241
1216
  * Provides intelligent CLI detection and MCP channel utilization
242
1217
  * when @lanonasis/cli v1.5.2+ is available in the environment
1218
+ *
1219
+ * IMPORTANT: This file imports Node.js modules and should only be used in Node.js environments
243
1220
  */
244
1221
  const execAsync = promisify(exec);
245
1222
  /**
@@ -313,8 +1290,10 @@ class CLIIntegration {
313
1290
  const { stdout: authOutput } = await execAsync('onasis auth status --output json 2>/dev/null || lanonasis auth status --output json 2>/dev/null', {
314
1291
  timeout: 3000
315
1292
  });
316
- const authStatus = JSON.parse(authOutput);
317
- authenticated = authStatus.authenticated === true;
1293
+ const parseResult = safeJsonParse(authOutput);
1294
+ if (parseResult.success) {
1295
+ authenticated = parseResult.data.authenticated === true;
1296
+ }
318
1297
  }
319
1298
  catch {
320
1299
  // Authentication check failed
@@ -336,10 +1315,10 @@ class CLIIntegration {
336
1315
  async executeCLICommand(command, options = {}) {
337
1316
  const cliInfo = await this.detectCLI();
338
1317
  if (!cliInfo.available) {
339
- return { error: 'CLI not available' };
1318
+ return { error: createErrorResponse('CLI not available', 'API_ERROR') };
340
1319
  }
341
1320
  if (!cliInfo.authenticated) {
342
- return { error: 'CLI not authenticated. Run: onasis login' };
1321
+ return { error: createErrorResponse('CLI not authenticated. Run: onasis login', 'AUTH_ERROR', 401) };
343
1322
  }
344
1323
  try {
345
1324
  const timeout = options.timeout || 30000;
@@ -356,22 +1335,20 @@ class CLIIntegration {
356
1335
  console.warn('CLI warning:', stderr);
357
1336
  }
358
1337
  if (outputFormat === 'json') {
359
- try {
360
- const result = JSON.parse(stdout);
361
- return { data: result };
362
- }
363
- catch (parseError) {
364
- return { error: `Failed to parse CLI JSON output: ${parseError instanceof Error ? parseError.message : 'Unknown error'}` };
1338
+ const parseResult = safeJsonParse(stdout);
1339
+ if (parseResult.success) {
1340
+ return { data: parseResult.data };
365
1341
  }
1342
+ return { error: createErrorResponse(parseResult.error, 'VALIDATION_ERROR', 400) };
366
1343
  }
367
1344
  return { data: stdout };
368
1345
  }
369
1346
  catch (error) {
370
1347
  if (error instanceof Error && error.message.includes('timeout')) {
371
- return { error: 'CLI command timeout' };
1348
+ return { error: createErrorResponse('CLI command timeout', 'TIMEOUT_ERROR', 408) };
372
1349
  }
373
1350
  return {
374
- error: error instanceof Error ? error.message : 'CLI command failed'
1351
+ error: createErrorResponse(error instanceof Error ? error.message : 'CLI command failed', 'API_ERROR')
375
1352
  };
376
1353
  }
377
1354
  }
@@ -439,14 +1416,14 @@ class CLIIntegration {
439
1416
  async getMCPStatus() {
440
1417
  const cliInfo = await this.detectCLI();
441
1418
  if (!cliInfo.mcpAvailable) {
442
- return { error: 'MCP not available via CLI' };
1419
+ return { error: createErrorResponse('MCP not available via CLI', 'API_ERROR') };
443
1420
  }
444
1421
  return this.executeCLICommand('mcp status');
445
1422
  }
446
1423
  async listMCPTools() {
447
1424
  const cliInfo = await this.detectCLI();
448
1425
  if (!cliInfo.mcpAvailable) {
449
- return { error: 'MCP not available via CLI' };
1426
+ return { error: createErrorResponse('MCP not available via CLI', 'API_ERROR') };
450
1427
  }
451
1428
  return this.executeCLICommand('mcp tools');
452
1429
  }
@@ -499,6 +1476,8 @@ class CLIIntegration {
499
1476
  *
500
1477
  * Intelligently routes requests through CLI v1.5.2+ when available,
501
1478
  * with fallback to direct API for maximum compatibility and performance
1479
+ *
1480
+ * IMPORTANT: This file uses Node.js-specific features (process.env) and should only be used in Node.js environments
502
1481
  */
503
1482
  /**
504
1483
  * Enhanced Memory Client with intelligent CLI/API routing
@@ -514,21 +1493,24 @@ class EnhancedMemoryClient {
514
1493
  }
515
1494
  constructor(config) {
516
1495
  this.capabilities = null;
517
- this.config = {
518
- preferCLI: true,
519
- enableMCP: true,
520
- cliDetectionTimeout: 5000,
521
- fallbackToAPI: true,
522
- minCLIVersion: '1.5.2',
523
- verbose: false,
524
- timeout: 30000,
525
- useGateway: true,
1496
+ // Merge config with defaults, ensuring all required fields are present
1497
+ // Spread config first, then apply defaults only for undefined values
1498
+ const mergedConfig = {
1499
+ ...config,
1500
+ preferCLI: config.preferCLI ?? true,
1501
+ enableMCP: config.enableMCP ?? true,
1502
+ cliDetectionTimeout: config.cliDetectionTimeout ?? 5000,
1503
+ fallbackToAPI: config.fallbackToAPI ?? true,
1504
+ minCLIVersion: config.minCLIVersion ?? '1.5.2',
1505
+ verbose: config.verbose ?? false,
1506
+ timeout: config.timeout ?? 30000,
1507
+ apiUrl: config.apiUrl || 'https://api.lanonasis.com',
526
1508
  apiKey: config.apiKey || process.env.LANONASIS_API_KEY || '',
527
1509
  authToken: config.authToken || '',
528
- headers: config.headers || {},
529
- ...config
1510
+ headers: config.headers || {}
530
1511
  };
531
- this.directClient = new MemoryClient(config);
1512
+ this.config = mergedConfig;
1513
+ this.directClient = new CoreMemoryClient(config);
532
1514
  this.cliIntegration = new CLIIntegration();
533
1515
  }
534
1516
  /**
@@ -623,7 +1605,7 @@ class EnhancedMemoryClient {
623
1605
  };
624
1606
  }
625
1607
  return {
626
- error: error instanceof Error ? error.message : `CLI ${operation} failed`,
1608
+ error: createErrorResponse(error instanceof Error ? error.message : `CLI ${operation} failed`, 'API_ERROR'),
627
1609
  source: 'cli',
628
1610
  mcpUsed: false
629
1611
  };
@@ -758,7 +1740,7 @@ class EnhancedMemoryClient {
758
1740
  }
759
1741
  catch (error) {
760
1742
  return {
761
- error: error instanceof Error ? error.message : 'Auth status check failed',
1743
+ error: createErrorResponse(error instanceof Error ? error.message : 'Auth status check failed', 'API_ERROR'),
762
1744
  source: 'cli',
763
1745
  mcpUsed: false
764
1746
  };
@@ -771,7 +1753,7 @@ class EnhancedMemoryClient {
771
1753
  const capabilities = await this.getCapabilities();
772
1754
  if (!capabilities.mcpSupport) {
773
1755
  return {
774
- error: 'MCP not available',
1756
+ error: createErrorResponse('MCP not available', 'API_ERROR'),
775
1757
  source: 'cli',
776
1758
  mcpUsed: false
777
1759
  };
@@ -782,7 +1764,7 @@ class EnhancedMemoryClient {
782
1764
  }
783
1765
  catch (error) {
784
1766
  return {
785
- error: error instanceof Error ? error.message : 'MCP status check failed',
1767
+ error: createErrorResponse(error instanceof Error ? error.message : 'MCP status check failed', 'API_ERROR'),
786
1768
  source: 'cli',
787
1769
  mcpUsed: false
788
1770
  };
@@ -822,218 +1804,29 @@ class EnhancedMemoryClient {
822
1804
  /**
823
1805
  * Factory function to create an enhanced memory client
824
1806
  */
825
- async function createEnhancedMemoryClient(config) {
1807
+ async function createNodeMemoryClient(config) {
826
1808
  const client = new EnhancedMemoryClient(config);
827
1809
  await client.initialize();
828
1810
  return client;
829
1811
  }
830
1812
 
831
1813
  /**
832
- * Configuration utilities for Memory Client SDK
833
- * Provides smart defaults and environment detection for CLI/MCP integration
834
- */
835
- /**
836
- * Environment detection utilities
837
- */
838
- const Environment = {
839
- isNode: typeof globalThis !== 'undefined' && 'process' in globalThis && globalThis.process?.versions?.node,
840
- isBrowser: typeof window !== 'undefined',
841
- isVSCode: typeof globalThis !== 'undefined' && 'vscode' in globalThis,
842
- isCursor: typeof globalThis !== 'undefined' && 'cursor' in globalThis,
843
- isWindsurf: typeof globalThis !== 'undefined' && 'windsurf' in globalThis,
844
- get isIDE() {
845
- return this.isVSCode || this.isCursor || this.isWindsurf;
846
- },
847
- get supportsCLI() {
848
- return Boolean(this.isNode && !this.isBrowser);
849
- }
850
- };
851
- /**
852
- * Create smart configuration with environment-aware defaults
853
- */
854
- function createSmartConfig(baseConfig, options = {}) {
855
- const defaults = {
856
- preferCLI: Environment.supportsCLI,
857
- minCLIVersion: '1.5.2',
858
- enableMCP: true,
859
- cliDetectionTimeout: 3000,
860
- verbose: false
861
- };
862
- const config = { ...defaults, ...options };
863
- const preferCLI = config.preferCLI ?? defaults.preferCLI ?? false;
864
- const minCLIVersion = config.minCLIVersion ?? defaults.minCLIVersion ?? '1.5.2';
865
- const enableMCP = config.enableMCP ?? defaults.enableMCP ?? true;
866
- const cliDetectionTimeout = config.cliDetectionTimeout ?? defaults.cliDetectionTimeout ?? 3000;
867
- const verbose = config.verbose ?? defaults.verbose ?? false;
868
- return {
869
- ...baseConfig,
870
- preferCLI,
871
- minCLIVersion,
872
- enableMCP,
873
- cliDetectionTimeout,
874
- verbose,
875
- // Smart API configuration with environment detection
876
- apiUrl: baseConfig.apiUrl || (process?.env?.NODE_ENV === 'development'
877
- ? 'http://localhost:3001'
878
- : 'https://api.lanonasis.com'),
879
- // Default timeout based on environment
880
- timeout: baseConfig.timeout || (Environment.isIDE ? 10000 : 15000)
881
- };
882
- }
883
- /**
884
- * Preset configurations for common scenarios
885
- */
886
- const ConfigPresets = {
887
- /**
888
- * Development configuration with local API and CLI preference
889
- */
890
- development: (apiKey) => createSmartConfig({
891
- apiUrl: 'http://localhost:3001',
892
- apiKey,
893
- timeout: 30000
894
- }, {
895
- preferCLI: true,
896
- verbose: true
897
- }),
898
- /**
899
- * Production configuration optimized for performance
900
- */
901
- production: (apiKey) => createSmartConfig({
902
- apiUrl: 'https://api.lanonasis.com',
903
- apiKey,
904
- timeout: 15000
905
- }, {
906
- preferCLI: Environment.supportsCLI,
907
- verbose: false
908
- }),
909
- /**
910
- * IDE extension configuration with MCP prioritization
911
- */
912
- ideExtension: (apiKey) => createSmartConfig({
913
- apiUrl: 'https://api.lanonasis.com',
914
- apiKey,
915
- timeout: 10000
916
- }, {
917
- preferCLI: true,
918
- enableMCP: true,
919
- cliDetectionTimeout: 2000
920
- }),
921
- /**
922
- * Browser-only configuration (no CLI support)
923
- */
924
- browserOnly: (apiKey) => createSmartConfig({
925
- apiUrl: 'https://api.lanonasis.com',
926
- apiKey,
927
- timeout: 15000
928
- }, {
929
- preferCLI: false,
930
- enableMCP: false
931
- }),
932
- /**
933
- * CLI-first configuration for server environments
934
- */
935
- serverCLI: (apiKey) => createSmartConfig({
936
- apiUrl: 'https://api.lanonasis.com',
937
- apiKey,
938
- timeout: 20000
939
- }, {
940
- preferCLI: true,
941
- enableMCP: true,
942
- verbose: false
943
- })
944
- };
945
- /**
946
- * Migration helper for existing MemoryClient users
1814
+ * @lanonasis/memory-client/node
1815
+ *
1816
+ * Node.js-enhanced client with CLI integration
1817
+ * ONLY import this in Node.js environments
1818
+ *
1819
+ * Provides intelligent CLI detection and MCP channel utilization
1820
+ * when @lanonasis/cli v1.5.2+ is available
947
1821
  */
948
- function migrateToEnhanced(existingConfig, enhancementOptions = {}) {
949
- return createSmartConfig(existingConfig, {
950
- preferCLI: Environment.supportsCLI,
951
- ...enhancementOptions
952
- });
953
- }
1822
+ // Enhanced client with CLI support
954
1823
 
955
- /**
956
- * Memory types supported by the service
957
- */
958
- const MEMORY_TYPES = ['context', 'project', 'knowledge', 'reference', 'personal', 'workflow'];
959
- /**
960
- * Memory status values
961
- */
962
- const MEMORY_STATUSES = ['active', 'archived', 'draft', 'deleted'];
963
- /**
964
- * Validation schemas using Zod
965
- */
966
- const createMemorySchema = z.object({
967
- title: z.string().min(1).max(500),
968
- content: z.string().min(1).max(50000),
969
- summary: z.string().max(1000).optional(),
970
- memory_type: z.enum(MEMORY_TYPES).default('context'),
971
- topic_id: z.string().uuid().optional(),
972
- project_ref: z.string().max(100).optional(),
973
- tags: z.array(z.string().min(1).max(50)).max(20).default([]),
974
- metadata: z.record(z.string(), z.unknown()).optional()
975
- });
976
- const updateMemorySchema = z.object({
977
- title: z.string().min(1).max(500).optional(),
978
- content: z.string().min(1).max(50000).optional(),
979
- summary: z.string().max(1000).optional(),
980
- memory_type: z.enum(MEMORY_TYPES).optional(),
981
- status: z.enum(MEMORY_STATUSES).optional(),
982
- topic_id: z.string().uuid().nullable().optional(),
983
- project_ref: z.string().max(100).nullable().optional(),
984
- tags: z.array(z.string().min(1).max(50)).max(20).optional(),
985
- metadata: z.record(z.string(), z.unknown()).optional()
986
- });
987
- const searchMemorySchema = z.object({
988
- query: z.string().min(1).max(1000),
989
- memory_types: z.array(z.enum(MEMORY_TYPES)).optional(),
990
- tags: z.array(z.string()).optional(),
991
- topic_id: z.string().uuid().optional(),
992
- project_ref: z.string().optional(),
993
- status: z.enum(MEMORY_STATUSES).default('active'),
994
- limit: z.number().int().min(1).max(100).default(20),
995
- threshold: z.number().min(0).max(1).default(0.7)
996
- });
997
- const createTopicSchema = z.object({
998
- name: z.string().min(1).max(100),
999
- description: z.string().max(500).optional(),
1000
- color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
1001
- icon: z.string().max(50).optional(),
1002
- parent_topic_id: z.string().uuid().optional()
1824
+ var index = /*#__PURE__*/Object.freeze({
1825
+ __proto__: null,
1826
+ CLIIntegration: CLIIntegration,
1827
+ EnhancedMemoryClient: EnhancedMemoryClient,
1828
+ createNodeMemoryClient: createNodeMemoryClient
1003
1829
  });
1004
1830
 
1005
- /**
1006
- * @lanonasis/memory-client
1007
- *
1008
- * Memory as a Service (MaaS) Client SDK for Lanonasis
1009
- * Intelligent memory management with semantic search capabilities
1010
- */
1011
- // Main client
1012
- // Constants
1013
- const VERSION = '1.0.0';
1014
- const CLIENT_NAME = '@lanonasis/memory-client';
1015
- // Environment detection
1016
- const isBrowser = typeof window !== 'undefined';
1017
- const isNode = typeof globalThis !== 'undefined' && 'process' in globalThis && globalThis.process?.versions?.node;
1018
- // Default configurations for different environments
1019
- const defaultConfigs = {
1020
- development: {
1021
- apiUrl: 'http://localhost:3001',
1022
- timeout: 30000,
1023
- useGateway: false
1024
- },
1025
- production: {
1026
- apiUrl: 'https://api.lanonasis.com',
1027
- timeout: 15000,
1028
- useGateway: true
1029
- },
1030
- gateway: {
1031
- apiUrl: 'https://api.lanonasis.com',
1032
- timeout: 10000,
1033
- useGateway: true
1034
- }
1035
- };
1036
- // Utility functions will be added in a future version to avoid circular imports
1037
-
1038
- export { CLIENT_NAME, CLIIntegration, ConfigPresets, EnhancedMemoryClient, Environment, MEMORY_STATUSES, MEMORY_TYPES, MemoryClient, VERSION, createEnhancedMemoryClient, createMemoryClient, createMemorySchema, createSmartConfig, createTopicSchema, defaultConfigs, isBrowser, isNode, migrateToEnhanced, searchMemorySchema, updateMemorySchema };
1831
+ 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 };
1039
1832
  //# sourceMappingURL=index.esm.js.map