task-engine-ai-core
Version:
Revolutionary AI-driven task management system with complete transformation trilogy: Frontend v0.1.0, Backend v0.2.0, CLI v0.3.0 - Enterprise-grade performance with 95% improvements
352 lines (307 loc) • 11.7 kB
JavaScript
/**
* IDE AI Provider
* Implements BaseAIProvider interface for IDE-based AI agents
*/
import { BaseAIProvider } from './base-provider.js';
import IDEAgentInterface from '../bridge/ide-agent-interface.js';
import BridgeConfig from '../bridge/bridge-config.js';
import logger from '../../mcp-server/src/logger.js';
export class IDEAIProvider extends BaseAIProvider {
constructor() {
super();
this.name = 'IDE Agent';
this.agentInterface = null;
this.bridgeConfig = null;
this.connected = false;
this.capabilities = new Set();
this.connectionAttempts = 0;
this.maxConnectionAttempts = 3;
this.reconnectDelay = 1000;
}
/**
* Override auth validation - IDE provider doesn't need API keys
*/
validateAuth(params) {
// IDE provider uses the IDE's built-in authentication
// No API key required
if (!this.connected) {
throw new Error('IDE agent not connected. Please ensure bridge is running.');
}
}
/**
* Get IDE agent client
*/
getClient(params) {
if (!this.agentInterface) {
throw new Error('IDE agent interface not initialized');
}
// Return a client-like interface that wraps the IDE agent
return {
interface: this.agentInterface,
ideType: params.ideType || 'cursor',
sessionId: params.sessionId
};
}
/**
* Initialize connection to IDE agent
*/
async initialize(options = {}) {
try {
// Load bridge configuration
this.bridgeConfig = new BridgeConfig();
await this.bridgeConfig.load();
// Auto-detect IDE if not specified
let ideType = options.ideType;
if (!ideType || ideType === 'auto-detect') {
const detection = await this.bridgeConfig.detectIDE();
ideType = detection.detected ? detection.type : 'cursor';
logger.info(`Auto-detected IDE: ${ideType}`);
}
// Initialize agent interface
this.agentInterface = new IDEAgentInterface({
ideType,
agentEndpoint: options.agentEndpoint,
sessionId: options.sessionId,
responseTimeout: this.bridgeConfig.get('ide.connectionTimeout') || 5000
});
await this.agentInterface.initialize();
this.connected = true;
this.capabilities = this.agentInterface.capabilities;
this.connectionAttempts = 0;
logger.info(`IDE AI Provider initialized for ${ideType}`);
return this;
} catch (error) {
this.connectionAttempts++;
logger.error(`Failed to initialize IDE AI Provider (attempt ${this.connectionAttempts}):`, error);
// Try to reconnect if enabled and within limits
if (this.connectionAttempts < this.maxConnectionAttempts &&
this.bridgeConfig?.get('bridge.autoRestart')) {
logger.info(`Retrying connection in ${this.reconnectDelay}ms...`);
await new Promise(resolve => setTimeout(resolve, this.reconnectDelay));
this.reconnectDelay *= 2; // Exponential backoff
return this.initialize(options);
}
throw error;
}
}
/**
* Generate text using IDE agent
*/
async generateText(params) {
try {
this.validateParams(params);
this.validateMessages(params.messages);
logger.debug(`Generating text via IDE agent with model: ${params.modelId}`);
const result = await this.agentInterface.sendRequest({
type: 'generate-text',
payload: {
messages: params.messages,
maxTokens: params.maxTokens,
temperature: params.temperature,
modelId: params.modelId
}
});
logger.debug('IDE agent generateText completed successfully');
return {
text: result.text,
usage: {
inputTokens: result.usage?.inputTokens || 0,
outputTokens: result.usage?.outputTokens || 0,
totalTokens: result.usage?.totalTokens || 0
},
model: result.model,
provider: 'ide-agent'
};
} catch (error) {
this.handleError('text generation via IDE agent', error);
}
}
/**
* Stream text using IDE agent
*/
async streamText(params) {
try {
this.validateParams(params);
this.validateMessages(params.messages);
logger.debug('Streaming text via IDE agent');
// For now, IDE agents may not support streaming
// Fall back to regular generation and simulate streaming
const result = await this.generateText(params);
// Create a simple stream-like interface
const stream = {
textStream: this.createTextStream(result.text),
usage: result.usage,
model: result.model,
provider: 'ide-agent'
};
logger.debug('IDE agent streamText completed successfully');
return stream;
} catch (error) {
this.handleError('text streaming via IDE agent', error);
}
}
/**
* Generate structured object using IDE agent
*/
async generateObject(params) {
try {
this.validateParams(params);
this.validateMessages(params.messages);
if (!params.schema) {
throw new Error('Schema is required for object generation');
}
if (!params.objectName) {
throw new Error('Object name is required for object generation');
}
logger.debug(`Generating object '${params.objectName}' via IDE agent`);
const result = await this.agentInterface.sendRequest({
type: 'generate-object',
payload: {
messages: params.messages,
schema: params.schema,
objectName: params.objectName,
maxTokens: params.maxTokens,
temperature: params.temperature,
modelId: params.modelId
}
});
logger.debug('IDE agent generateObject completed successfully');
return {
object: result.object,
usage: {
inputTokens: result.usage?.inputTokens || 0,
outputTokens: result.usage?.outputTokens || 0,
totalTokens: result.usage?.totalTokens || 0
},
model: result.model,
provider: 'ide-agent'
};
} catch (error) {
this.handleError('object generation via IDE agent', error);
}
}
/**
* Create a simple text stream from complete text
*/
createTextStream(text) {
const chunks = text.split(' ');
let index = 0;
return {
async *[Symbol.asyncIterator]() {
for (const chunk of chunks) {
yield { type: 'text-delta', textDelta: chunk + ' ' };
// Small delay to simulate streaming
await new Promise(resolve => setTimeout(resolve, 10));
}
yield { type: 'finish', finishReason: 'stop' };
}
};
}
/**
* Check if IDE agent supports a specific capability
*/
hasCapability(capability) {
return this.capabilities.has(capability);
}
/**
* Get available models from IDE
*/
async getAvailableModels() {
if (!this.agentInterface) {
return [];
}
// This would query the IDE for available models
// For now, return common IDE models
const ideModels = {
cursor: [
{ id: 'cursor-claude-3.5-sonnet', name: 'Claude 3.5 Sonnet (Cursor)' },
{ id: 'cursor-gpt-4', name: 'GPT-4 (Cursor)' },
{ id: 'cursor-claude-3-opus', name: 'Claude 3 Opus (Cursor)' }
],
vscode: [
{ id: 'copilot-gpt-4', name: 'GitHub Copilot GPT-4' },
{ id: 'copilot-gpt-3.5', name: 'GitHub Copilot GPT-3.5' }
],
windsurf: [
{ id: 'windsurf-claude-3.5-sonnet', name: 'Claude 3.5 Sonnet (Windsurf)' },
{ id: 'windsurf-gpt-4', name: 'GPT-4 (Windsurf)' }
]
};
const ideType = this.agentInterface.ideType;
return ideModels[ideType] || [];
}
/**
* Get current IDE agent status
*/
getStatus() {
return {
name: this.name,
connected: this.connected,
ideType: this.agentInterface?.ideType || 'unknown',
capabilities: Array.from(this.capabilities),
activeModel: this.agentInterface?.getActiveModel() || 'unknown',
agentStatus: this.agentInterface?.getStatus() || null
};
}
/**
* Disconnect from IDE agent
*/
async disconnect() {
if (this.agentInterface) {
await this.agentInterface.disconnect();
}
this.connected = false;
this.capabilities.clear();
logger.info('IDE AI Provider disconnected');
}
/**
* Test connection to IDE agent
*/
async testConnection() {
try {
if (!this.connected) {
throw new Error('Not connected to IDE agent');
}
const testResult = await this.generateText({
modelId: 'test',
messages: [{ role: 'user', content: 'Test connection' }],
maxTokens: 10,
temperature: 0.1
});
return {
success: true,
model: testResult.model,
provider: testResult.provider,
responseTime: Date.now()
};
} catch (error) {
return {
success: false,
error: error.message,
responseTime: Date.now()
};
}
}
/**
* Override error handling to provide IDE-specific context
*/
handleError(operation, error) {
const ideContext = this.agentInterface ? {
ideType: this.agentInterface.ideType,
connected: this.connected,
capabilities: Array.from(this.capabilities)
} : {};
logger.error(`IDE AI Provider error during ${operation}:`, {
error: error.message,
stack: error.stack,
ideContext
});
// Enhance error message with IDE-specific guidance
let enhancedMessage = error.message;
if (!this.connected) {
enhancedMessage += ' (IDE agent not connected - ensure WebSocket bridge is running)';
}
throw new Error(enhancedMessage);
}
}
export default IDEAIProvider;