diff --git a/src/vs/workbench/contrib/agent/browser/agentService.ts b/src/vs/workbench/contrib/agent/browser/agentService.ts new file mode 100644 index 00000000000..cf6b0217f14 --- /dev/null +++ b/src/vs/workbench/contrib/agent/browser/agentService.ts @@ -0,0 +1,63 @@ +import { IAgentService, IAgent, AgentRole } from 'vs/workbench/contrib/agent/common/agentService'; +import { ITaskService } from 'vs/workbench/contrib/agent/common/taskService'; +import { IContextService } from 'vs/workbench/contrib/agent/common/contextService'; +import { IModelService } from 'vs/workbench/contrib/agent/common/modelService'; +import { ICodeIndexService } from 'vs/workbench/contrib/agent/common/codeIndexService'; +import { IKnowledgeGraphService } from 'vs/workbench/contrib/agent/common/knowledgeGraphService'; +import { ITask } from 'vs/workbench/contrib/agent/common/task'; +import { Task } from 'vs/workbench/contrib/agent/browser/task'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +class Agent implements IAgent { + constructor( + public readonly role: AgentRole, + private readonly taskService: ITaskService, + private readonly instantiationService: IInstantiationService + ) {} + + startTask(text: string, images?: string[]): Promise { + const task = this.instantiationService.createInstance(Task, this, this.role, text, images); + // In a real implementation, we would manage the task lifecycle here. + (task as Task).run(); + return Promise.resolve(task); + } +} + +export class AgentService implements IAgentService { + _serviceBrand: undefined; + + private readonly agents: Map = new Map(); + + constructor( + @ITaskService private readonly taskService: ITaskService, + @IContextService private readonly contextService: IContextService, + @IModelService private readonly modelService: IModelService, + @ICodeIndexService private readonly codeIndexService: ICodeIndexService, + @IKnowledgeGraphService private readonly knowledgeGraphService: IKnowledgeGraphService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + this.agents.set('architect', new Agent('architect', this.taskService, this.instantiationService)); + this.agents.set('developer', new Agent('developer', this.taskService, this.instantiationService)); + this.agents.set('tester', new Agent('tester', this.taskService, this.instantiationService)); + } + + getAgent(role: AgentRole): IAgent | undefined { + return this.agents.get(role); + } + + async startTask(role: AgentRole, text: string, images?: string[]): Promise { + const agent = this.getAgent(role); + if (!agent) { + throw new Error(`Agent with role '${role}' not found.`); + } + return agent.startTask(text, images); + } + + async cancelTask(taskId: string): Promise { + await this.taskService.cancelTask(); + } + + getCurrentTask(): ITask | undefined { + return this.taskService.getCurrentTask(); + } +} diff --git a/src/vs/workbench/contrib/agent/browser/agentView.ts b/src/vs/workbench/contrib/agent/browser/agentView.ts new file mode 100644 index 00000000000..fec621b9bd7 --- /dev/null +++ b/src/vs/workbench/contrib/agent/browser/agentView.ts @@ -0,0 +1,107 @@ +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWebviewViewService, IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { URI } from 'vs/base/common/uri'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IAgentService } from 'vs/workbench/contrib/agent/common/agentService'; + +export class AgentView extends ViewPane { + private webviewViewService: IWebviewViewService; + private webview?: IWebviewService; + private environmentService: IEnvironmentService; + private agentService: IAgentService; + + constructor( + options: IViewPaneOptions, + @IKeybindingService keybindingService: IKeybindingService, + @IContextMenuService contextMenuService: IContextMenuService, + @IConfigurationService configurationService: IConfigurationService, + @IInstantiationService instantiationService: IInstantiationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IOpenerService openerService: IOpenerService, + @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, + @IWebviewViewService webviewViewService: IWebviewViewService, + @IContextKeyService contextKeyService: IContextKeyService, + @IStorageService storageService: IStorageService, + @IEnvironmentService environmentService: IEnvironmentService, + @IAgentService agentService: IAgentService, + ) { + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService, viewDescriptorService, themeService, telemetryService, openerService); + this.webviewViewService = webviewViewService; + this.environmentService = environmentService; + this.agentService = agentService; + } + + protected override renderBody(container: HTMLElement): void { + super.renderBody(container); + + const webviewView = this.webviewViewService.createWebviewView(this.id, { + title: this.title, + icon: this.icon, + }); + + this.webview = webviewView.webview; + webviewView.webview.html = this.getHtmlForWebview(); + + webviewView.webview.onDidReceiveMessage(message => { + switch (message.type) { + case 'startTask': + this.agentService.startTask('developer', message.text, message.images); + break; + } + }); + + // Post initial state + this.postStateToWebview(); + } + + private async postStateToWebview() { + if (!this.webview) { + return; + } + + const state = { + // This will be populated with the actual state from the services + }; + + this.webview.postMessage({ type: 'state', state }); + } + + private getHtmlForWebview(): string { + if (!this.webview) { + return ''; + } + + const webviewUiUri = this.webview.asWebviewUri( + URI.joinPath(this.environmentService.appRoot, 'src', 'vs', 'workbench', 'contrib', 'agent', 'webview-ui', 'build', 'assets', 'index.js') + ); + + return ` + + + + + Cortex IDE Agent + + +
+ + + `; + } + + protected override layoutBody(height: number, width: number): void { + super.layoutBody(height, width); + } +} diff --git a/src/vs/workbench/contrib/agent/browser/codeIndexService.ts b/src/vs/workbench/contrib/agent/browser/codeIndexService.ts new file mode 100644 index 00000000000..6fb2224cb2b --- /dev/null +++ b/src/vs/workbench/contrib/agent/browser/codeIndexService.ts @@ -0,0 +1,72 @@ +import { ICodeIndexService, ISymbolReference } from 'vs/workbench/contrib/agent/common/codeIndexService'; +import { VectorStoreSearchResult } from 'vs/workbench/contrib/agent/common/vectorStore'; +import { IContextService } from 'vs/workbench/contrib/agent/common/contextService'; +import { IModelService } from 'vs/workbench/contrib/agent/common/modelService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { URI } from 'vs/base/common/uri'; + +// This would be a more complex implementation in a real scenario, +// likely involving a language server and a vector database. +class LanguageService { + async findSymbol(query: string): Promise { return []; } + async findReferences(symbol: ISymbolReference): Promise { return []; } + async insertAfterSymbol(symbol: ISymbolReference, content: string): Promise {} +} + +class VectorDB { + async search(query: string): Promise { return []; } + async startIndexing(): Promise {} + async stopIndexing(): Promise {} + async clearIndex(): Promise {} +} + +export class CodeIndexService implements ICodeIndexService { + _serviceBrand: undefined; + + private languageService: LanguageService; + private vectorDB: VectorDB; + + constructor( + @IContextService private readonly contextService: IContextService, + @IModelService private readonly modelService: IModelService, + @ILogService private readonly logService: ILogService + ) { + this.languageService = new LanguageService(); + this.vectorDB = new VectorDB(); + } + + async startIndexing(): Promise { + this.logService.info('Starting code indexing...'); + await this.vectorDB.startIndexing(); + } + + stopWatcher(): void { + this.logService.info('Stopping code indexing watcher...'); + this.vectorDB.stopIndexing(); + } + + async clearIndexData(): Promise { + this.logService.info('Clearing code index data...'); + await this.vectorDB.clearIndex(); + } + + async searchIndex(query: string, directoryPrefix?: string): Promise { + this.logService.info(`Searching index for: ${query}`); + return this.vectorDB.search(query); + } + + async findSymbol(query: string): Promise { + this.logService.info(`Finding symbol: ${query}`); + return this.languageService.findSymbol(query); + } + + async findReferences(symbol: ISymbolReference): Promise { + this.logService.info(`Finding references for symbol: ${symbol}`); + return this.languageService.findReferences(symbol); + } + + async insertAfterSymbol(symbol: ISymbolReference, content: string): Promise { + this.logService.info(`Inserting content after symbol: ${symbol}`); + await this.languageService.insertAfterSymbol(symbol, content); + } +} diff --git a/src/vs/workbench/contrib/agent/browser/contextService.ts b/src/vs/workbench/contrib/agent/browser/contextService.ts new file mode 100644 index 00000000000..198a3351473 --- /dev/null +++ b/src/vs/workbench/contrib/agent/browser/contextService.ts @@ -0,0 +1,62 @@ +import { IContextService } from 'vs/workbench/contrib/agent/common/contextService'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { GlobalState, RooCodeSettings, GLOBAL_STATE_KEYS, SECRET_STATE_KEYS, isSecretStateKey } from '@roo-code/types'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Memento } from 'vs/workbench/common/memento'; + +export class ContextService implements IContextService { + _serviceBrand: undefined; + + private stateCache: GlobalState = {}; + private memento: Memento; + + constructor( + @IStorageService private readonly storageService: IStorageService, + @ILogService private readonly logService: ILogService + ) { + this.memento = new Memento('cortex-agent', this.storageService); + this.loadState(); + } + + private loadState(): void { + const state = this.memento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + for (const key of GLOBAL_STATE_KEYS) { + this.stateCache[key] = state[key]; + } + } + + private saveState(): void { + const state = this.memento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + for (const key of GLOBAL_STATE_KEYS) { + state[key] = this.stateCache[key]; + } + this.memento.saveMemento(); + } + + getValue(key: K): RooCodeSettings[K] { + if (isSecretStateKey(key)) { + this.logService.warn(`Attempted to access secret key '${key}' through non-secret method.`); + return undefined; + } + return this.stateCache[key as keyof GlobalState] as RooCodeSettings[K]; + } + + async setValue(key: K, value: RooCodeSettings[K]): Promise { + if (isSecretStateKey(key)) { + this.logService.warn(`Attempted to set secret key '${key}' through non-secret method.`); + return; + } + this.stateCache[key as keyof GlobalState] = value; + this.saveState(); + } + + getValues(): RooCodeSettings { + return this.stateCache as RooCodeSettings; + } + + async setValues(values: RooCodeSettings): Promise { + for (const key in values) { + await this.setValue(key as keyof RooCodeSettings, values[key as keyof RooCodeSettings]); + } + } +} diff --git a/src/vs/workbench/contrib/agent/browser/knowledgeGraphService.ts b/src/vs/workbench/contrib/agent/browser/knowledgeGraphService.ts new file mode 100644 index 00000000000..e83a824ad0e --- /dev/null +++ b/src/vs/workbench/contrib/agent/browser/knowledgeGraphService.ts @@ -0,0 +1,36 @@ +import { IKnowledgeGraphService } from 'vs/workbench/contrib/agent/common/knowledgeGraphService'; +import { ICodeIndexService } from 'vs/workbench/contrib/agent/common/codeIndexService'; +import { ILogService } from 'vs/platform/log/common/log'; + +// This would be a more complex implementation in a real scenario, +// likely involving a graph database and natural language processing. +class GraphDB { + async buildGraph(data: any): Promise {} + async queryGraph(query: string): Promise { return []; } +} + +export class KnowledgeGraphService implements IKnowledgeGraphService { + _serviceBrand: undefined; + + private graphDB: GraphDB; + + constructor( + @ICodeIndexService private readonly codeIndexService: ICodeIndexService, + @ILogService private readonly logService: ILogService + ) { + this.graphDB = new GraphDB(); + } + + async buildGraph(): Promise { + this.logService.info('Building knowledge graph...'); + // In a real implementation, we would use the code index to get + // information about the codebase and then use that to build the graph. + const symbols = await this.codeIndexService.findSymbol('*'); + await this.graphDB.buildGraph(symbols); + } + + async queryGraph(query: string): Promise { + this.logService.info(`Querying knowledge graph with: ${query}`); + return this.graphDB.queryGraph(query); + } +} diff --git a/src/vs/workbench/contrib/agent/browser/modelService.ts b/src/vs/workbench/contrib/agent/browser/modelService.ts new file mode 100644 index 00000000000..cf122721d16 --- /dev/null +++ b/src/vs/workbench/contrib/agent/browser/modelService.ts @@ -0,0 +1,75 @@ +import { IModelService } from 'vs/workbench/contrib/agent/common/modelService'; +import { IContextService } from 'vs/workbench/contrib/agent/common/contextService'; +import { ProviderSettings, ProviderSettingsEntry, Mode } from '@roo-code/types'; +import { Emitter, Event } from 'vs/base/common/event'; + +export class ModelService implements IModelService { + _serviceBrand: undefined; + + private readonly _onDidActivateProviderProfile = new Emitter<{ name: string; provider?: string }>(); + public readonly onDidActivateProviderProfile: Event<{ name: string; provider?: string }> = this._onDidActivateProviderProfile.event; + + constructor(@IContextService private readonly contextService: IContextService) {} + + getProviderProfiles(): ProviderSettingsEntry[] { + return this.contextService.getValue('listApiConfigMeta') || []; + } + + getProviderProfile(name: string): ProviderSettingsEntry | undefined { + return this.getProviderProfiles().find((profile) => profile.name === name); + } + + async upsertProviderProfile(name: string, providerSettings: ProviderSettings, activate: boolean = true): Promise { + const profiles = this.getProviderProfiles(); + const id = Math.random().toString(36).substring(7); + const newProfile: ProviderSettingsEntry = { + id, + name, + apiProvider: providerSettings.apiProvider, + }; + + const existingIndex = profiles.findIndex(p => p.name === name); + if (existingIndex !== -1) { + newProfile.id = profiles[existingIndex].id; + profiles[existingIndex] = newProfile; + } else { + profiles.push(newProfile); + } + + await this.contextService.setValue('listApiConfigMeta', profiles); + // In a real implementation, we would save the full providerSettings + // using a secure method. + + if (activate) { + await this.activateProviderProfile({ name }); + } + + return id; + } + + async deleteProviderProfile(profileToDelete: ProviderSettingsEntry): Promise { + let profiles = this.getProviderProfiles(); + profiles = profiles.filter(p => p.id !== profileToDelete.id); + await this.contextService.setValue('listApiConfigMeta', profiles); + + const currentProfileName = this.contextService.getValue('currentApiConfigName'); + if (currentProfileName === profileToDelete.name) { + const newProfileToActivate = profiles[0]; + if (newProfileToActivate) { + await this.activateProviderProfile({ name: newProfileToActivate.name }); + } + } + } + + async activateProviderProfile(args: { name: string } | { id: string }): Promise { + const profile = 'name' in args + ? this.getProviderProfile(args.name) + : this.getProviderProfiles().find(p => p.id === args.id); + + if (profile) { + await this.contextService.setValue('currentApiConfigName', profile.name); + // In a real implementation, we would load the full provider settings here. + this._onDidActivateProviderProfile.fire({ name: profile.name, provider: profile.apiProvider }); + } + } +} diff --git a/src/vs/workbench/contrib/agent/browser/task.ts b/src/vs/workbench/contrib/agent/browser/task.ts new file mode 100644 index 00000000000..caddba69d1a --- /dev/null +++ b/src/vs/workbench/contrib/agent/browser/task.ts @@ -0,0 +1,35 @@ +import { ITask } from "vs/workbench/contrib/agent/common/task"; +import { AgentRole, IAgentService } from "vs/workbench/contrib/agent/common/agentService"; +import { IModelService } from "vs/workbench/contrib/agent/common/modelService"; +import { ICodeIndexService } from "vs/workbench/contrib/agent/common/codeIndexService"; +import { IKnowledgeGraphService } from "vs/workbench/contrib/agent/common/knowledgeGraphService"; + +export class Task implements ITask { + readonly taskId: string; + readonly rootTaskId?: string; + readonly parentTaskId?: string; + childTaskId?: string; + + constructor( + private readonly agentService: IAgentService, + private readonly modelService: IModelService, + private readonly codeIndexService: ICodeIndexService, + private readonly knowledgeGraphService: IKnowledgeGraphService, + public readonly role: AgentRole, + public readonly text: string, + public readonly images?: string[] + ) { + this.taskId = Math.random().toString(36).substring(7); + } + + async run(): Promise { + // This is where the core agent logic will go. + // For now, we'll just log a message. + console.log(`Running task ${this.taskId} with role ${this.role} and text "${this.text}"`); + + // Example of how the agent might use the other services: + const profile = this.modelService.getProviderProfile(this.modelService.getProviderProfiles()[0].name); + const symbols = await this.codeIndexService.findSymbol('myFunction'); + const graphResults = await this.knowledgeGraphService.queryGraph('What is the purpose of myFunction?'); + } +} diff --git a/src/vs/workbench/contrib/agent/browser/taskService.ts b/src/vs/workbench/contrib/agent/browser/taskService.ts new file mode 100644 index 00000000000..cfaae1d134e --- /dev/null +++ b/src/vs/workbench/contrib/agent/browser/taskService.ts @@ -0,0 +1,31 @@ +import { ITaskService } from 'vs/workbench/contrib/agent/common/taskService'; +import { ITask } from 'vs/workbench/contrib/agent/common/task'; +import { IAgentService, AgentRole } from 'vs/workbench/contrib/agent/common/agentService'; +import { RooCodeSettings, CreateTaskOptions } from '@roo-code/types'; +import { Task } from 'vs/workbench/contrib/agent/browser/task'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +export class TaskService implements ITaskService { + _serviceBrand: undefined; + + private readonly taskStack: ITask[] = []; + + constructor( + @IInstantiationService private readonly instantiationService: IInstantiationService + ) {} + + async createTask(text?: string, images?: string[], parentTask?: ITask, options?: CreateTaskOptions, configuration?: RooCodeSettings): Promise { + const agentService = this.instantiationService.invokeFunction(accessor => accessor.get(IAgentService)); + const task = this.instantiationService.createInstance(Task, agentService, 'developer', text || '', images); + this.taskStack.push(task); + return task; + } + + async cancelTask(): Promise { + this.taskStack.pop(); + } + + getCurrentTask(): ITask | undefined { + return this.taskStack[this.taskStack.length - 1]; + } +} diff --git a/src/vs/workbench/contrib/agent/common/agent.contribution.ts b/src/vs/workbench/contrib/agent/common/agent.contribution.ts new file mode 100644 index 00000000000..3f4a9ad0518 --- /dev/null +++ b/src/vs/workbench/contrib/agent/common/agent.contribution.ts @@ -0,0 +1,43 @@ +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IAgentService } from 'vs/workbench/contrib/agent/common/agentService'; +import { IContextService } from 'vs/workbench/contrib/agent/common/contextService'; +import { IModelService } from 'vs/workbench/contrib/agent/common/modelService'; +import { ICodeIndexService } from 'vs/workbench/contrib/agent/common/codeIndexService'; +import { ITaskService } from 'vs/workbench/contrib/agent/common/taskService'; +import { IKnowledgeGraphService } from 'vs/workbench/contrib/agent/common/knowledgeGraphService'; +import { AgentService } from 'vs/workbench/contrib/agent/browser/agentService'; +import { ContextService } from 'vs/workbench/contrib/agent/browser/contextService'; +import { ModelService } from 'vs/workbench/contrib/agent/browser/modelService'; +import { CodeIndexService } from 'vs/workbench/contrib/agent/browser/codeIndexService'; +import { TaskService } from 'vs/workbench/contrib/agent/browser/taskService'; +import { KnowledgeGraphService } from 'vs/workbench/contrib/agent/browser/knowledgeGraphService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IViewsRegistry, Extensions, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; +import { AgentView } from 'vs/workbench/contrib/agent/browser/agentView'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { Codicon } from 'vs/base/common/codicons'; + +registerSingleton(IAgentService, AgentService, InstantiationType.Delayed); +registerSingleton(IContextService, ContextService, InstantiationType.Delayed); +registerSingleton(IModelService, ModelService, InstantiationType.Delayed); +registerSingleton(ICodeIndexService, CodeIndexService, InstantiationType.Delayed); +registerSingleton(ITaskService, TaskService, InstantiationType.Delayed); +registerSingleton(IKnowledgeGraphService, KnowledgeGraphService, InstantiationType.Delayed); + +const container: ViewContainer = Registry.as(Extensions.ViewsRegistry).registerViewContainer({ + id: 'cortex-agent', + title: 'Cortex Agent', + icon: Codicon.hubot, + order: 100, +}, ViewContainerLocation.Sidebar); + +Registry.as(Extensions.ViewsRegistry).registerViews([{ + id: 'cortex-agent-view', + name: 'Cortex Agent', + containerIcon: Codicon.hubot, + ctorDescriptor: new SyncDescriptor(AgentView), + canToggleVisibility: true, + canMoveView: true, + collapsed: false, + order: 1, +}], container); diff --git a/src/vs/workbench/contrib/agent/common/agentService.ts b/src/vs/workbench/contrib/agent/common/agentService.ts new file mode 100644 index 00000000000..ac3fb0a78c0 --- /dev/null +++ b/src/vs/workbench/contrib/agent/common/agentService.ts @@ -0,0 +1,21 @@ +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { ITask } from 'vs/workbench/contrib/agent/common/task'; + +export type AgentRole = 'architect' | 'developer' | 'tester'; + +export interface IAgent { + readonly role: AgentRole; + startTask(text: string, images?: string[]): Promise; +} + +export const IAgentService = createDecorator('agentService'); + +export interface IAgentService extends IWorkbenchContribution { + _serviceBrand: undefined; + + getAgent(role: AgentRole): IAgent | undefined; + startTask(role: AgentRole, text: string, images?: string[]): Promise; + cancelTask(taskId: string): Promise; + getCurrentTask(): ITask | undefined; +} diff --git a/src/vs/workbench/contrib/agent/common/codeIndexService.ts b/src/vs/workbench/contrib/agent/common/codeIndexService.ts new file mode 100644 index 00000000000..577a61384fb --- /dev/null +++ b/src/vs/workbench/contrib/agent/common/codeIndexService.ts @@ -0,0 +1,29 @@ +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { VectorStoreSearchResult } from 'vs/workbench/contrib/agent/common/vectorStore'; +import { URI } from 'vs/base/common/uri'; + +export interface ISymbolReference { + uri: URI; + range: { + startLineNumber: number; + startColumn: number; + endLineNumber: number; + endColumn: number; + }; +} + +export const ICodeIndexService = createDecorator('codeIndexService'); + +export interface ICodeIndexService extends IWorkbenchContribution { + _serviceBrand: undefined; + + startIndexing(): Promise; + stopWatcher(): void; + clearIndexData(): Promise; + searchIndex(query: string, directoryPrefix?: string): Promise; + + findSymbol(query: string): Promise; + findReferences(symbol: ISymbolReference): Promise; + insertAfterSymbol(symbol: ISymbolReference, content: string): Promise; +} diff --git a/src/vs/workbench/contrib/agent/common/contextService.ts b/src/vs/workbench/contrib/agent/common/contextService.ts new file mode 100644 index 00000000000..0d3064dc54f --- /dev/null +++ b/src/vs/workbench/contrib/agent/common/contextService.ts @@ -0,0 +1,14 @@ +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { GlobalState, RooCodeSettings } from '@roo-code/types'; + +export const IContextService = createDecorator('contextService'); + +export interface IContextService extends IWorkbenchContribution { + _serviceBrand: undefined; + + getValue(key: K): RooCodeSettings[K]; + setValue(key: K, value: RooCodeSettings[K]): Promise; + getValues(): RooCodeSettings; + setValues(values: RooCodeSettings): Promise; +} diff --git a/src/vs/workbench/contrib/agent/common/knowledgeGraphService.ts b/src/vs/workbench/contrib/agent/common/knowledgeGraphService.ts new file mode 100644 index 00000000000..b6090404273 --- /dev/null +++ b/src/vs/workbench/contrib/agent/common/knowledgeGraphService.ts @@ -0,0 +1,11 @@ +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; + +export const IKnowledgeGraphService = createDecorator('knowledgeGraphService'); + +export interface IKnowledgeGraphService extends IWorkbenchContribution { + _serviceBrand: undefined; + + buildGraph(): Promise; + queryGraph(query: string): Promise; +} diff --git a/src/vs/workbench/contrib/agent/common/modelService.ts b/src/vs/workbench/contrib/agent/common/modelService.ts new file mode 100644 index 00000000000..dbf809cc635 --- /dev/null +++ b/src/vs/workbench/contrib/agent/common/modelService.ts @@ -0,0 +1,15 @@ +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { ProviderSettings, ProviderSettingsEntry } from '@roo-code/types'; + +export const IModelService = createDecorator('modelService'); + +export interface IModelService extends IWorkbenchContribution { + _serviceBrand: undefined; + + getProviderProfiles(): ProviderSettingsEntry[]; + getProviderProfile(name: string): ProviderSettingsEntry | undefined; + upsertProviderProfile(name: string, providerSettings: ProviderSettings, activate?: boolean): Promise; + deleteProviderProfile(profileToDelete: ProviderSettingsEntry): Promise; + activateProviderProfile(args: { name: string } | { id: string }): Promise; +} diff --git a/src/vs/workbench/contrib/agent/common/task.ts b/src/vs/workbench/contrib/agent/common/task.ts new file mode 100644 index 00000000000..a8ff20dd49f --- /dev/null +++ b/src/vs/workbench/contrib/agent/common/task.ts @@ -0,0 +1,6 @@ +export interface ITask { + readonly taskId: string; + readonly rootTaskId?: string; + readonly parentTaskId?: string; + childTaskId?: string; +} diff --git a/src/vs/workbench/contrib/agent/common/taskService.ts b/src/vs/workbench/contrib/agent/common/taskService.ts new file mode 100644 index 00000000000..3a74ba0a4b3 --- /dev/null +++ b/src/vs/workbench/contrib/agent/common/taskService.ts @@ -0,0 +1,14 @@ +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { ITask } from 'vs/workbench/contrib/agent/common/task'; +import { RooCodeSettings, CreateTaskOptions } from '@roo-code/types'; + +export const ITaskService = createDecorator('taskService'); + +export interface ITaskService extends IWorkbenchContribution { + _serviceBrand: undefined; + + createTask(text?: string, images?: string[], parentTask?: ITask, options?: CreateTaskOptions, configuration?: RooCodeSettings): Promise; + cancelTask(): Promise; + getCurrentTask(): ITask | undefined; +} diff --git a/src/vs/workbench/contrib/agent/common/vectorStore.ts b/src/vs/workbench/contrib/agent/common/vectorStore.ts new file mode 100644 index 00000000000..343173b1133 --- /dev/null +++ b/src/vs/workbench/contrib/agent/common/vectorStore.ts @@ -0,0 +1,5 @@ +export interface VectorStoreSearchResult { + filePath: string; + content: string; + score: number; +} diff --git a/src/vs/workbench/contrib/agent/webview-ui/index.html b/src/vs/workbench/contrib/agent/webview-ui/index.html new file mode 100644 index 00000000000..ada964b2429 --- /dev/null +++ b/src/vs/workbench/contrib/agent/webview-ui/index.html @@ -0,0 +1,12 @@ + + + + + + Cortex IDE Agent + + +
+ + + diff --git a/src/vs/workbench/contrib/agent/webview-ui/package.json b/src/vs/workbench/contrib/agent/webview-ui/package.json new file mode 100644 index 00000000000..cb6720e2b5d --- /dev/null +++ b/src/vs/workbench/contrib/agent/webview-ui/package.json @@ -0,0 +1,108 @@ +{ + "name": "@roo-code/vscode-webview", + "private": true, + "type": "module", + "scripts": { + "lint": "eslint src --ext=ts,tsx --max-warnings=0", + "check-types": "tsc", + "pretest": "turbo run bundle --cwd ..", + "test": "vitest run", + "format": "prettier --write src", + "dev": "vite", + "build": "tsc -b && vite build", + "build:nightly": "tsc -b && vite build --mode nightly", + "preview": "vite preview", + "clean": "rimraf ../src/webview-ui ../apps/vscode-nightly/build/webview-ui tsconfig.tsbuildinfo .turbo" + }, + "dependencies": { + "@radix-ui/react-alert-dialog": "^1.1.6", + "@radix-ui/react-checkbox": "^1.1.5", + "@radix-ui/react-collapsible": "^1.1.3", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.5", + "@radix-ui/react-icons": "^1.3.2", + "@radix-ui/react-popover": "^1.1.6", + "@radix-ui/react-portal": "^1.1.5", + "@radix-ui/react-progress": "^1.1.2", + "@radix-ui/react-select": "^2.1.6", + "@radix-ui/react-separator": "^1.1.2", + "@radix-ui/react-slider": "^1.2.3", + "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-tooltip": "^1.1.8", + "@roo-code/types": "workspace:^", + "@tailwindcss/vite": "^4.0.0", + "@tanstack/react-query": "^5.68.0", + "@types/qrcode": "^1.5.5", + "@vscode/codicons": "^0.0.36", + "@vscode/webview-ui-toolkit": "^1.4.0", + "axios": "^1.12.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.0.0", + "date-fns": "^4.1.0", + "debounce": "^2.1.1", + "diff": "^5.2.0", + "fast-deep-equal": "^3.1.3", + "fzf": "^0.5.2", + "hast-util-to-jsx-runtime": "^2.3.6", + "i18next": "^25.0.0", + "i18next-http-backend": "^3.0.2", + "katex": "^0.16.11", + "knuth-shuffle-seeded": "^1.0.6", + "lru-cache": "^11.1.0", + "lucide-react": "^0.518.0", + "mermaid": "^11.4.1", + "posthog-js": "^1.227.2", + "pretty-bytes": "^7.0.0", + "qrcode": "^1.5.4", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-i18next": "^15.4.1", + "react-markdown": "^9.0.3", + "react-remark": "^2.1.0", + "react-textarea-autosize": "^8.5.3", + "react-use": "^17.5.1", + "react-virtuoso": "^4.7.13", + "rehype-highlight": "^7.0.0", + "rehype-katex": "^7.0.1", + "remark-gfm": "^4.0.1", + "remark-math": "^6.0.0", + "remove-markdown": "^0.6.0", + "shell-quote": "^1.8.2", + "shiki": "^3.2.1", + "source-map": "^0.7.4", + "stacktrace-js": "^2.0.2", + "styled-components": "^6.1.13", + "tailwind-merge": "^3.0.0", + "tailwindcss": "^4.0.0", + "tailwindcss-animate": "^1.0.7", + "unist-util-visit": "^5.0.0", + "use-sound": "^5.0.0", + "vscode-material-icons": "^0.1.1", + "vscrui": "^0.2.2", + "zod": "^3.25.61" + }, + "devDependencies": { + "@roo-code/config-eslint": "workspace:^", + "@roo-code/config-typescript": "workspace:^", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.2.0", + "@testing-library/user-event": "^14.6.1", + "@types/diff": "^5.2.1", + "@types/jest": "^29.0.0", + "@types/katex": "^0.16.7", + "@types/node": "20.x", + "@types/react": "^18.3.23", + "@types/react-dom": "^18.3.5", + "@types/shell-quote": "^1.7.5", + "@types/stacktrace-js": "^2.0.3", + "@types/vscode-webview": "^1.57.5", + "@vitejs/plugin-react": "^4.3.4", + "@vitest/ui": "^3.2.3", + "identity-obj-proxy": "^3.0.0", + "jsdom": "^26.0.0", + "typescript": "5.8.3", + "vite": "6.3.6", + "vitest": "^3.2.3" + } +} diff --git a/src/vs/workbench/contrib/agent/webview-ui/src/App.tsx b/src/vs/workbench/contrib/agent/webview-ui/src/App.tsx new file mode 100644 index 00000000000..2a69e390e54 --- /dev/null +++ b/src/vs/workbench/contrib/agent/webview-ui/src/App.tsx @@ -0,0 +1,61 @@ +import React, { useCallback, useEffect, useState } from "react" +import { QueryClient, QueryClientProvider } from "@tanstack/react-query" + +// This will be replaced with a new communication mechanism +const vscode = { + postMessage: (message: any) => { + // In a real browser environment, this would be `window.vscode.postMessage` + console.log("Message to workbench:", message); + window.postMessage(message, '*'); + } +}; + +const App = () => { + const [state, setState] = useState(null); + const [inputText, setInputText] = useState(""); + + useEffect(() => { + const handleMessage = (event: any) => { + const message = event.data; + if (message.type === 'state') { + setState(message.state); + } + }; + window.addEventListener("message", handleMessage); + return () => window.removeEventListener("message", handleMessage); + }, []); + + const handleStartTask = () => { + vscode.postMessage({ type: 'startTask', text: inputText }); + }; + + if (!state) { + return
Loading...
; + } + + return ( +
+

Cortex IDE Agent

+ {/* We will add the chat history here */} + +
+