@lanonasis/cli 3.9.14 → 3.9.15

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog - @lanonasis/cli
2
2
 
3
+ ## [3.9.15] - 2026-04-04
4
+
5
+ ### 🐛 Bug Fixes
6
+
7
+ - **Displayed memory ID prefixes now work across CLI memory reads and deletes**: `lanonasis memory get`, `memory update`, and `memory delete` now resolve 8+ character prefixes from the list output before calling the live API, so operators no longer need to paste full UUIDs for routine follow-up actions.
8
+ - **Legacy memory route drift no longer breaks prefix fetches**: Memory lookups now retry against the compatibility `GET /api/v1/memory/get?id=...` and delete fallback routes only when the canonical plural endpoint returns the known `Memory ID is required` validation drift.
9
+ - **MCP memory tools now accept both `id` and `memory_id`**: The bundled LanOnasis MCP server and remote MCP bridge now normalize legacy `memory_id` callers onto the live `id` contract, which restores OpenClaw and similar clients when they pass the older field name.
10
+
3
11
  ## [3.9.14] - 2026-04-03
4
12
 
5
13
  ### 🐛 Bug Fixes
@@ -46,6 +46,7 @@ export declare class LanonasisMCPServer {
46
46
  private transportFailures;
47
47
  private enableFallback;
48
48
  constructor(options?: LanonasisServerOptions);
49
+ private extractMemoryIdentifier;
49
50
  /**
50
51
  * Initialize the server
51
52
  */
@@ -43,6 +43,15 @@ export class LanonasisMCPServer {
43
43
  // Setup error handling
44
44
  this.setupErrorHandling();
45
45
  }
46
+ extractMemoryIdentifier(args) {
47
+ const id = typeof args.id === 'string' ? args.id.trim() : '';
48
+ const legacyId = typeof args.memory_id === 'string' ? args.memory_id.trim() : '';
49
+ const resolved = id || legacyId;
50
+ if (!resolved) {
51
+ throw new Error('Memory ID is required');
52
+ }
53
+ return resolved;
54
+ }
46
55
  /**
47
56
  * Initialize the server
48
57
  */
@@ -170,12 +179,19 @@ export class LanonasisMCPServer {
170
179
  inputSchema: {
171
180
  type: 'object',
172
181
  properties: {
182
+ id: {
183
+ type: 'string',
184
+ description: 'Memory ID or displayed prefix'
185
+ },
173
186
  memory_id: {
174
187
  type: 'string',
175
- description: 'Memory ID'
188
+ description: 'Legacy alias for memory ID or displayed prefix'
176
189
  }
177
190
  },
178
- required: ['memory_id']
191
+ anyOf: [
192
+ { required: ['id'] },
193
+ { required: ['memory_id'] }
194
+ ]
179
195
  }
180
196
  },
181
197
  {
@@ -184,9 +200,13 @@ export class LanonasisMCPServer {
184
200
  inputSchema: {
185
201
  type: 'object',
186
202
  properties: {
203
+ id: {
204
+ type: 'string',
205
+ description: 'Memory ID or displayed prefix'
206
+ },
187
207
  memory_id: {
188
208
  type: 'string',
189
- description: 'Memory ID'
209
+ description: 'Legacy alias for memory ID or displayed prefix'
190
210
  },
191
211
  title: {
192
212
  type: 'string',
@@ -202,7 +222,10 @@ export class LanonasisMCPServer {
202
222
  description: 'New tags (optional)'
203
223
  }
204
224
  },
205
- required: ['memory_id']
225
+ anyOf: [
226
+ { required: ['id'] },
227
+ { required: ['memory_id'] }
228
+ ]
206
229
  }
207
230
  },
208
231
  {
@@ -211,12 +234,19 @@ export class LanonasisMCPServer {
211
234
  inputSchema: {
212
235
  type: 'object',
213
236
  properties: {
237
+ id: {
238
+ type: 'string',
239
+ description: 'Memory ID or displayed prefix'
240
+ },
214
241
  memory_id: {
215
242
  type: 'string',
216
- description: 'Memory ID'
243
+ description: 'Legacy alias for memory ID or displayed prefix'
217
244
  }
218
245
  },
219
- required: ['memory_id']
246
+ anyOf: [
247
+ { required: ['id'] },
248
+ { required: ['memory_id'] }
249
+ ]
220
250
  }
221
251
  },
222
252
  // Topic tools
@@ -647,11 +677,13 @@ Please choose an option (1-4):`
647
677
  topic_id: args.topic_id
648
678
  });
649
679
  case 'memory_get':
650
- return await this.apiClient.getMemory(args.memory_id);
680
+ return await this.apiClient.getMemory(this.extractMemoryIdentifier(args));
651
681
  case 'memory_update':
652
- return await this.apiClient.updateMemory(args.memory_id, args);
682
+ const memoryId = this.extractMemoryIdentifier(args);
683
+ const { id: _id, memory_id: _legacyId, ...updateArgs } = args;
684
+ return await this.apiClient.updateMemory(memoryId, updateArgs);
653
685
  case 'memory_delete':
654
- return await this.apiClient.deleteMemory(args.memory_id);
686
+ return await this.apiClient.deleteMemory(this.extractMemoryIdentifier(args));
655
687
  // Topic operations
656
688
  case 'topic_create':
657
689
  return await this.apiClient.createTopic(args);
@@ -198,6 +198,8 @@ export declare class APIClient {
198
198
  private shouldUseLegacyMemoryRpcFallback;
199
199
  private shouldRetryViaApiGateway;
200
200
  private shouldRetryViaSupabaseMemoryFunctions;
201
+ private isUuid;
202
+ resolveMemoryId(idOrPrefix: string): Promise<string>;
201
203
  private shouldUsePostListFallback;
202
204
  private getSupabaseFunctionsBaseUrl;
203
205
  private mapMemoryApiRouteToSupabaseFunctions;
package/dist/utils/api.js CHANGED
@@ -10,6 +10,7 @@ const MEMORY_TYPES = [
10
10
  'personal',
11
11
  'workflow'
12
12
  ];
13
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
13
14
  export class APIClient {
14
15
  client;
15
16
  config;
@@ -166,6 +167,61 @@ export class APIClient {
166
167
  const hasVendorKey = this.config.hasVendorKey();
167
168
  return hasVendorKey || hasOpaqueToken || authMethod === 'oauth' || authMethod === 'oauth2';
168
169
  }
170
+ isUuid(value) {
171
+ return UUID_PATTERN.test(value);
172
+ }
173
+ async resolveMemoryId(idOrPrefix) {
174
+ const candidate = idOrPrefix.trim();
175
+ if (!candidate) {
176
+ throw new Error('Memory ID is required.');
177
+ }
178
+ if (this.isUuid(candidate)) {
179
+ return candidate;
180
+ }
181
+ if (candidate.length < 8) {
182
+ throw new Error('Memory ID prefix must be at least 8 characters or a full UUID.');
183
+ }
184
+ const matches = new Set();
185
+ const normalizedCandidate = candidate.toLowerCase();
186
+ const limit = 100;
187
+ let page = 1;
188
+ while (true) {
189
+ const result = await this.getMemories({ page, limit });
190
+ const memories = result.memories || result.data || [];
191
+ if (memories.length === 0) {
192
+ break;
193
+ }
194
+ for (const memory of memories) {
195
+ if (memory.id.toLowerCase().startsWith(normalizedCandidate)) {
196
+ matches.add(memory.id);
197
+ }
198
+ }
199
+ const pagination = result.pagination || {
200
+ total: memories.length,
201
+ limit,
202
+ offset: 0,
203
+ has_more: false,
204
+ };
205
+ const totalPages = Number.isFinite(Number(pagination.pages))
206
+ ? Number(pagination.pages)
207
+ : Math.max(1, Math.ceil(Number(pagination.total || memories.length) / limit));
208
+ const hasMore = typeof pagination.has_more === 'boolean'
209
+ ? pagination.has_more
210
+ : page < totalPages;
211
+ if (!hasMore) {
212
+ break;
213
+ }
214
+ page += 1;
215
+ }
216
+ const resolvedMatches = [...matches];
217
+ if (resolvedMatches.length === 0) {
218
+ throw new Error(`Memory not found for ID/prefix: ${candidate}`);
219
+ }
220
+ if (resolvedMatches.length > 1) {
221
+ throw new Error(`Memory ID prefix is ambiguous: ${candidate}. Matches: ${resolvedMatches.slice(0, 5).join(', ')}`);
222
+ }
223
+ return resolvedMatches[0];
224
+ }
169
225
  shouldUsePostListFallback(error) {
170
226
  const status = Number(error?.response?.status || 0);
171
227
  if (status === 405)
@@ -627,13 +683,16 @@ export class APIClient {
627
683
  }
628
684
  }
629
685
  async getMemory(id) {
686
+ const resolvedId = await this.resolveMemoryId(id);
630
687
  try {
631
- const response = await this.client.get(`/api/v1/memories/${id}`);
688
+ const response = await this.client.get(`/api/v1/memories/${encodeURIComponent(resolvedId)}`);
632
689
  return this.normalizeMemoryEntry(response.data);
633
690
  }
634
691
  catch (error) {
635
692
  if (this.shouldUseLegacyMemoryRpcFallback(error)) {
636
- const fallback = await this.client.post('/api/v1/memory/get', { id });
693
+ const fallback = await this.client.get('/api/v1/memory/get', {
694
+ params: { id: resolvedId }
695
+ });
637
696
  const payload = fallback.data && typeof fallback.data === 'object'
638
697
  ? fallback.data.data ?? fallback.data
639
698
  : fallback.data;
@@ -643,14 +702,15 @@ export class APIClient {
643
702
  }
644
703
  }
645
704
  async updateMemory(id, data) {
705
+ const resolvedId = await this.resolveMemoryId(id);
646
706
  try {
647
- const response = await this.client.put(`/api/v1/memories/${id}`, data);
707
+ const response = await this.client.put(`/api/v1/memories/${encodeURIComponent(resolvedId)}`, data);
648
708
  return this.normalizeMemoryEntry(response.data);
649
709
  }
650
710
  catch (error) {
651
711
  if (this.shouldUseLegacyMemoryRpcFallback(error) || error?.response?.status === 404) {
652
712
  const fallback = await this.client.post('/api/v1/memory/update', {
653
- id,
713
+ id: resolvedId,
654
714
  ...data
655
715
  });
656
716
  const payload = fallback.data && typeof fallback.data === 'object'
@@ -662,12 +722,15 @@ export class APIClient {
662
722
  }
663
723
  }
664
724
  async deleteMemory(id) {
725
+ const resolvedId = await this.resolveMemoryId(id);
665
726
  try {
666
- await this.client.delete(`/api/v1/memories/${id}`);
727
+ await this.client.delete(`/api/v1/memories/${encodeURIComponent(resolvedId)}`);
667
728
  }
668
729
  catch (error) {
669
730
  if (this.shouldUseLegacyMemoryRpcFallback(error) || error?.response?.status === 404) {
670
- await this.client.post('/api/v1/memory/delete', { id });
731
+ await this.client.delete('/api/v1/memory/delete', {
732
+ params: { id: resolvedId }
733
+ });
671
734
  return;
672
735
  }
673
736
  throw error;
@@ -5,6 +5,13 @@ import { CLIConfig } from './config.js';
5
5
  import * as fs from 'fs';
6
6
  import { EventSource } from 'eventsource';
7
7
  import WebSocket from 'ws';
8
+ function getMemoryToolId(args) {
9
+ const id = typeof args.id === 'string' ? args.id.trim() : '';
10
+ if (id)
11
+ return id;
12
+ const legacyId = typeof args.memory_id === 'string' ? args.memory_id.trim() : '';
13
+ return legacyId || undefined;
14
+ }
8
15
  export class MCPClient {
9
16
  client = null;
10
17
  config;
@@ -838,6 +845,7 @@ export class MCPClient {
838
845
  endpoint: '/memory/{id}',
839
846
  transform: (args) => {
840
847
  const data = { ...args };
848
+ delete data.id;
841
849
  delete data.memory_id;
842
850
  return data;
843
851
  }
@@ -861,9 +869,9 @@ export class MCPClient {
861
869
  const axios = (await import('axios')).default;
862
870
  // Handle dynamic endpoint for memory operations that need ID
863
871
  let endpoint = mapping.endpoint;
864
- if (endpoint.includes('{id}') && args.memory_id) {
865
- // Ensure memory_id is treated as a string for replacement
866
- endpoint = endpoint.replace('{id}', String(args.memory_id));
872
+ const memoryId = getMemoryToolId(args);
873
+ if (endpoint.includes('{id}') && memoryId) {
874
+ endpoint = endpoint.replace('{id}', encodeURIComponent(memoryId));
867
875
  }
868
876
  const response = await axios({
869
877
  method: mapping.method,
@@ -873,7 +881,7 @@ export class MCPClient {
873
881
  'Content-Type': 'application/json'
874
882
  },
875
883
  data: mapping.transform ? mapping.transform(args) : undefined,
876
- params: mapping.method === 'GET' ? args : undefined
884
+ params: mapping.method === 'GET' && !mapping.endpoint.includes('{id}') ? args : undefined
877
885
  });
878
886
  return response.data;
879
887
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lanonasis/cli",
3
- "version": "3.9.14",
3
+ "version": "3.9.15",
4
4
  "description": "Professional CLI for LanOnasis Memory as a Service (MaaS) with MCP support, seamless inline editing, and enterprise-grade security",
5
5
  "keywords": [
6
6
  "lanonasis",