lanonasis-memory
Version:
Memory as a Service integration - AI-powered memory management with semantic search (Compatible with CLI v3.0.6+)
431 lines • 17.2 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.EnhancedMemoryService = void 0;
const vscode = __importStar(require("vscode"));
let cachedMemoryClientModule;
let attemptedMemoryClientLoad = false;
function getMemoryClientModule() {
if (!attemptedMemoryClientLoad) {
attemptedMemoryClientLoad = true;
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
cachedMemoryClientModule = require('@lanonasis/memory-client');
}
catch (error) {
console.warn('[EnhancedMemoryService] @lanonasis/memory-client not available. Falling back to basic service.', error);
cachedMemoryClientModule = undefined;
}
}
return cachedMemoryClientModule;
}
class EnhancedMemoryService {
constructor(secureApiKeyService) {
this.client = null;
this.cliCapabilities = null;
const sdkModule = getMemoryClientModule();
if (!sdkModule) {
throw new Error('@lanonasis/memory-client module not available');
}
this.sdk = sdkModule;
this.secureApiKeyService = secureApiKeyService;
this.config = vscode.workspace.getConfiguration('lanonasis');
this.showPerformanceFeedback = this.config.get('showPerformanceFeedback', false);
// Create status bar item to show CLI/MCP status
this.statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
this.statusBarItem.command = 'lanonasis.showConnectionInfo';
this.initializeClient();
}
async initializeClient() {
const { Environment, EnhancedMemoryClient } = this.sdk;
const credential = await this.secureApiKeyService.getStoredCredentials();
if (!credential) {
this.client = null;
this.updateStatusBar(false, 'No API Key');
return;
}
try {
// Use IDE extension preset for optimized configuration
const clientConfig = this.buildClientConfigFromCredential(credential);
// Override with VSCode-specific settings
const apiUrl = this.config.get('apiUrl', 'https://mcp.lanonasis.com');
const useGateway = this.config.get('useGateway', true);
clientConfig.apiUrl = useGateway ?
this.config.get('gatewayUrl', 'https://mcp.lanonasis.com') :
apiUrl;
// Enable CLI detection with shorter timeout for responsive UX
clientConfig.preferCLI = Environment.supportsCLI;
clientConfig.cliDetectionTimeout = 2000;
clientConfig.verbose = this.config.get('verboseLogging', false);
// Performance warning: verbose logging in production
if (clientConfig.verbose && process.env.NODE_ENV === 'production') {
vscode.window.showWarningMessage('Verbose logging is enabled in production. This may impact performance and expose sensitive information in logs.');
console.warn('[EnhancedMemoryService] Warning: Verbose logging is enabled in production. This may impact performance and expose sensitive information in logs.');
}
this.client = new EnhancedMemoryClient(clientConfig);
// Initialize and detect CLI capabilities
await this.client.initialize();
this.cliCapabilities = await this.detectCapabilities();
this.updateStatusBar(true, this.getConnectionStatus());
}
catch (error) {
console.warn('Enhanced Memory Service initialization failed:', error);
this.client = null;
this.updateStatusBar(false, 'Initialization Failed');
throw error;
}
}
async detectCapabilities() {
if (!this.client) {
return {
cliAvailable: false,
mcpSupport: false,
authenticated: false,
goldenContract: false
};
}
try {
// Test if CLI integration is working
const testRequest = this.toSDKSearchRequest({
query: 'test connection',
limit: 1,
status: 'active',
threshold: 0.1
});
const testResult = await this.client.searchMemories(testRequest);
return {
cliAvailable: testResult.source === 'cli',
mcpSupport: testResult.mcpUsed || false,
authenticated: testResult.error === undefined,
goldenContract: testResult.source === 'cli' // CLI available implies Golden Contract v1.5.2+
};
}
catch {
return {
cliAvailable: false,
mcpSupport: false,
authenticated: false,
goldenContract: false
};
}
}
getConnectionStatus() {
if (!this.cliCapabilities)
return 'Unknown';
if (this.cliCapabilities.cliAvailable) {
const parts = ['CLI'];
if (this.cliCapabilities.mcpSupport)
parts.push('MCP');
if (this.cliCapabilities.goldenContract)
parts.push('Golden');
return parts.join('+');
}
return 'API';
}
updateStatusBar(connected, status) {
if (connected) {
this.statusBarItem.text = `$(database) ${status}`;
this.statusBarItem.backgroundColor = undefined;
this.statusBarItem.tooltip = `Lanonasis Memory: Connected via ${status}`;
}
else {
this.statusBarItem.text = `$(alert) ${status}`;
this.statusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.errorBackground');
this.statusBarItem.tooltip = `Lanonasis Memory: ${status}`;
}
this.statusBarItem.show();
}
async refreshClient() {
this.config = vscode.workspace.getConfiguration('lanonasis');
await this.initializeClient();
}
async refreshConfig() {
await this.refreshClient();
}
isAuthenticated() {
return this.client !== null;
}
getCapabilities() {
return this.cliCapabilities;
}
async testConnection(apiKey) {
const { EnhancedMemoryClient } = this.sdk;
let testClient = this.client;
if (apiKey) {
const config = this.buildClientConfigFromCredential({ type: 'apiKey', token: apiKey });
testClient = new EnhancedMemoryClient(config);
await testClient.initialize();
}
if (!testClient) {
const credential = await this.secureApiKeyService.getStoredCredentials();
if (!credential) {
throw new Error('No API key configured');
}
const config = this.buildClientConfigFromCredential(credential);
testClient = new EnhancedMemoryClient(config);
await testClient.initialize();
}
// Test with enhanced client - this will try CLI first, then fallback to API
const testRequest = this.toSDKSearchRequest({
query: 'connection test',
limit: 1,
status: 'active',
threshold: 0.1
});
const result = await testClient.searchMemories(testRequest);
if (result.error) {
throw new Error(result.error);
}
// Update capabilities after successful test
if (!apiKey) {
this.cliCapabilities = await this.detectCapabilities();
this.updateStatusBar(true, this.getConnectionStatus());
}
}
async createMemory(memory) {
if (!this.client) {
throw new Error('Not authenticated. Please configure your API key.');
}
const sdkMemory = this.toSDKCreateRequest(memory);
const result = await this.client.createMemory(sdkMemory);
if (result.error || !result.data) {
throw new Error(result.error || 'Failed to create memory');
}
this.showOperationFeedback('create', result);
return this.convertSDKMemoryEntry(result.data);
}
async searchMemories(query, options = {}) {
if (!this.client) {
throw new Error('Not authenticated. Please configure your API key.');
}
const searchRequest = {
query,
limit: 20,
threshold: 0.7,
status: 'active',
...options
};
const sdkSearchRequest = this.toSDKSearchRequest(searchRequest);
const result = await this.client.searchMemories(sdkSearchRequest);
if (result.error || !result.data) {
throw new Error(result.error || 'Search failed');
}
// Show search performance info in verbose mode
if (this.config.get('verboseLogging', false)) {
this.showOperationFeedback('search', result);
}
return this.convertSDKSearchResults(result.data.results);
}
async getMemory(id) {
if (!this.client) {
throw new Error('Not authenticated. Please configure your API key.');
}
const result = await this.client.getMemory(id);
if (result.error || !result.data) {
throw new Error(result.error || 'Memory not found');
}
return this.convertSDKMemoryEntry(result.data);
}
async listMemories(limit = 50) {
if (!this.client) {
throw new Error('Not authenticated. Please configure your API key.');
}
// Type validation for limit parameter
if (typeof limit !== 'number' || limit < 0) {
throw new Error('limit must be a non-negative number');
}
// Ensure limit is within reasonable bounds
const validatedLimit = Math.min(Math.max(1, Math.floor(limit)), 1000);
const result = await this.client.listMemories({
limit: validatedLimit,
sort: 'updated_at',
order: 'desc'
});
if (result.error || !result.data) {
throw new Error(result.error || 'Failed to fetch memories');
}
return result.data.data.map(entry => this.convertSDKMemoryEntry(entry));
}
async deleteMemory(id) {
if (!this.client) {
throw new Error('Not authenticated. Please configure your API key.');
}
const result = await this.client.deleteMemory(id);
if (result.error) {
throw new Error(result.error);
}
this.showOperationFeedback('delete', result);
}
async getMemoryStats() {
if (!this.client) {
throw new Error('Not authenticated. Please configure your API key.');
}
const result = await this.client.getMemoryStats();
if (result.error || !result.data) {
throw new Error(result.error || 'Failed to fetch stats');
}
return this.convertSDKUserMemoryStats(result.data);
}
showOperationFeedback(operation, result) {
if (!this.showPerformanceFeedback)
return;
const source = result.source === 'cli' ?
(result.mcpUsed ? 'CLI+MCP' : 'CLI') :
'API';
const message = `${operation} completed via ${source}`;
// Show brief status message
vscode.window.setStatusBarMessage(`$(check) ${message}`, 2000);
}
async showConnectionInfo() {
const caps = this.cliCapabilities;
if (!caps) {
vscode.window.showInformationMessage('Connection status: Unknown');
return;
}
const details = [
`CLI Available: ${caps.cliAvailable ? '✅' : '❌'}`,
`MCP Support: ${caps.mcpSupport ? '✅' : '❌'}`,
`Authenticated: ${caps.authenticated ? '✅' : '❌'}`,
`Golden Contract: ${caps.goldenContract ? '✅' : '❌'}`
];
const message = `Lanonasis Memory Connection Status:\n\n${details.join('\n')}`;
if (caps.cliAvailable && caps.goldenContract) {
vscode.window.showInformationMessage(`${message}\n\nEnhanced performance with CLI v1.5.2+ integration!`);
}
else if (caps.authenticated) {
vscode.window.showInformationMessage(`${message}\n\nInstall /cli v1.5.2+ for enhanced performance.`);
}
else {
vscode.window.showWarningMessage(message);
}
}
toSDKCreateRequest(memory) {
const { memory_type, ...rest } = memory;
return {
...rest,
memory_type: this.mapMemoryType(memory_type)
};
}
toSDKSearchRequest(request) {
const { memory_types, ...rest } = request;
const sdkTypes = memory_types?.map(type => this.mapMemoryType(type));
const sdkRequest = {
...rest,
...(sdkTypes ? { memory_types: sdkTypes } : {})
};
return sdkRequest;
}
mapMemoryType(vscodeType) {
const typeMap = {
conversation: 'context',
knowledge: 'knowledge',
project: 'project',
context: 'context',
reference: 'reference',
personal: 'personal',
workflow: 'workflow'
};
return typeMap[vscodeType] ?? 'context';
}
mapMemoryTypeFromSDK(sdkType) {
const typeMap = {
context: 'context',
project: 'project',
knowledge: 'knowledge',
reference: 'reference',
personal: 'personal',
workflow: 'workflow',
conversation: 'conversation'
};
return typeMap[sdkType] ?? 'context';
}
convertSDKMemoryEntry(sdkEntry) {
return {
...sdkEntry,
memory_type: this.mapMemoryTypeFromSDK(sdkEntry.memory_type)
};
}
buildClientConfigFromCredential(credential) {
const { ConfigPresets } = this.sdk;
const config = ConfigPresets.ideExtension(credential.type === 'apiKey' ? credential.token : undefined);
if (credential.type === 'oauth') {
config.apiKey = undefined;
config.authToken = credential.token;
}
return config;
}
convertSDKSearchResults(sdkResults) {
return sdkResults.map(result => ({
...result,
memory_type: this.mapMemoryTypeFromSDK(result.memory_type)
}));
}
convertSDKUserMemoryStats(stats) {
const initial = {
conversation: 0,
knowledge: 0,
project: 0,
context: 0,
reference: 0,
personal: 0,
workflow: 0
};
const memoriesByType = { ...initial };
for (const [key, value] of Object.entries(stats.memories_by_type)) {
const mappedKey = this.mapMemoryTypeFromSDK(key);
memoriesByType[mappedKey] = value;
}
return {
...stats,
memories_by_type: memoriesByType
};
}
dispose() {
this.statusBarItem.dispose();
}
// Migration helper for existing MemoryService users
static async migrateFromBasicService(secureApiKeyService) {
const enhanced = new EnhancedMemoryService(secureApiKeyService);
// Show migration message
vscode.window.showInformationMessage('🚀 Upgraded to Enhanced Memory Service with CLI integration!', 'Learn More').then(selection => {
if (selection === 'Learn More') {
vscode.env.openExternal(vscode.Uri.parse('https://docs.lanonasis.com/cli/integration'));
}
});
return enhanced;
}
}
exports.EnhancedMemoryService = EnhancedMemoryService;
//# sourceMappingURL=EnhancedMemoryService.js.map