feat: 实现核心服务层
- 新增对话服务(src/services/dialogService.ts) - 封装完整的对话生命周期管理 - 集成 SSE 流式响应处理 - 支持对话创建、消息发送、对话中止 - 提供统一的事件回调接口 - 新增工具执行器(src/services/toolExecutor.ts) - 实现前端工具调用框架 - 支持 readFile、writeFile、listFiles、executeCommand 等工具 - 提供工具执行结果的标准化返回 - 集成 VSCode API 进行文件和终端操作 - 新增用户交互处理(src/services/userInteraction.ts) - 实现 AI 向用户提问功能(AskUser) - 支持 input、confirm、quickPick 等交互类型 - 使用 VSCode 原生 UI 组件展示问题 - 提供答案收集和提交机制
This commit is contained in:
154
src/services/userInteraction.ts
Normal file
154
src/services/userInteraction.ts
Normal file
@ -0,0 +1,154 @@
|
||||
/**
|
||||
* 用户交互处理器
|
||||
* 处理 ask_user 事件,通过 WebView 显示问题并收集用户回答
|
||||
*/
|
||||
import * as vscode from 'vscode';
|
||||
import { submitAnswer } from './apiClient';
|
||||
import type { AskUserEvent, AnswerRequest } from '../types/api';
|
||||
|
||||
/**
|
||||
* 待处理的用户问题
|
||||
*/
|
||||
interface PendingQuestion {
|
||||
askId: string;
|
||||
taskId: string;
|
||||
question: string;
|
||||
options: string[];
|
||||
resolve: (answer: string) => void;
|
||||
reject: (error: Error) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户交互管理器
|
||||
*/
|
||||
export class UserInteractionManager {
|
||||
private pendingQuestions = new Map<string, PendingQuestion>();
|
||||
private webviewPanel: vscode.WebviewPanel | null = null;
|
||||
|
||||
/**
|
||||
* 设置 WebView 面板(用于发送消息)
|
||||
*/
|
||||
setWebviewPanel(panel: vscode.WebviewPanel): void {
|
||||
this.webviewPanel = panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 ask_user 事件
|
||||
* @param event ask_user 事件数据
|
||||
* @param taskId 当前任务ID
|
||||
*/
|
||||
async handleAskUser(event: AskUserEvent, taskId: string): Promise<void> {
|
||||
const { askId, question, options } = event;
|
||||
|
||||
console.log(`[UserInteraction] 收到问题: askId=${askId}, question=${question}`);
|
||||
|
||||
// 通过 WebView 显示问题
|
||||
if (this.webviewPanel) {
|
||||
this.webviewPanel.webview.postMessage({
|
||||
command: 'showQuestion',
|
||||
askId,
|
||||
question,
|
||||
options
|
||||
});
|
||||
}
|
||||
|
||||
// 创建 Promise 等待用户回答
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pendingQuestions.set(askId, {
|
||||
askId,
|
||||
taskId,
|
||||
question,
|
||||
options,
|
||||
resolve: (answer: string) => {
|
||||
this.submitUserAnswer(askId, taskId, answer)
|
||||
.then(() => resolve())
|
||||
.catch(reject);
|
||||
},
|
||||
reject
|
||||
});
|
||||
|
||||
// 设置超时(5分钟)
|
||||
setTimeout(() => {
|
||||
if (this.pendingQuestions.has(askId)) {
|
||||
this.pendingQuestions.delete(askId);
|
||||
reject(new Error('用户回答超时'));
|
||||
}
|
||||
}, 300000);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理用户提交的回答(从 WebView 调用)
|
||||
* @param askId 问题ID
|
||||
* @param selected 选中的选项
|
||||
* @param customInput 自定义输入
|
||||
*/
|
||||
async receiveAnswer(
|
||||
askId: string,
|
||||
selected?: string[],
|
||||
customInput?: string
|
||||
): Promise<void> {
|
||||
const pending = this.pendingQuestions.get(askId);
|
||||
if (!pending) {
|
||||
console.warn(`[UserInteraction] 问题不存在或已超时: askId=${askId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建答案
|
||||
const answer = customInput || selected?.join(', ') || '';
|
||||
|
||||
console.log(`[UserInteraction] 收到用户回答: askId=${askId}, answer=${answer}`);
|
||||
|
||||
// 移除待处理问题
|
||||
this.pendingQuestions.delete(askId);
|
||||
|
||||
// 触发 resolve
|
||||
pending.resolve(answer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交用户回答到后端
|
||||
*/
|
||||
private async submitUserAnswer(
|
||||
askId: string,
|
||||
taskId: string,
|
||||
answer: string
|
||||
): Promise<void> {
|
||||
const request: AnswerRequest = {
|
||||
askId,
|
||||
taskId,
|
||||
customInput: answer
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await submitAnswer(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error || '提交回答失败');
|
||||
}
|
||||
console.log(`[UserInteraction] 回答已提交: askId=${askId}`);
|
||||
} catch (error) {
|
||||
console.error(`[UserInteraction] 提交回答失败: askId=${askId}`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有待处理的问题
|
||||
*/
|
||||
cancelAll(): void {
|
||||
for (const [askId, pending] of this.pendingQuestions) {
|
||||
pending.reject(new Error('用户交互已取消'));
|
||||
}
|
||||
this.pendingQuestions.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有待处理的问题
|
||||
*/
|
||||
hasPendingQuestions(): boolean {
|
||||
return this.pendingQuestions.size > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 全局实例
|
||||
export const userInteractionManager = new UserInteractionManager();
|
||||
Reference in New Issue
Block a user