Files
IC-Coder-Plugin/src/services/userInteraction.ts
XiaoFeng f933d84cd1 feat: 新增会话压缩命令和上下文显示功能
- ICHelperPanel: 新增 compressConversation 命令处理,支持手动触发会话压缩
- ICHelperPanel: 在加载历史会话时设置 lastTaskId,确保压缩操作可用
- webviewContent: 新增 contextUsage 消息处理,更新上下文使用量显示
- userInteraction: 将用户回答超时时间从 5 分钟延长至 2 小时
2025-12-31 18:50:27 +08:00

180 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 用户交互处理器
* 处理 ask_user 事件,通过 WebView 显示问题并收集用户回答
*/
import * as vscode from 'vscode';
import { submitAnswer, submitToolConfirm } 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;
}
/**
* 获取 WebView 面板
*/
getWebviewPanel(): vscode.WebviewPanel | null {
return this.webviewPanel;
}
/**
* 处理 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}`);
// 注意:问题显示已经通过 dialogService 的 onSegmentUpdate 统一处理
// 这里不再单独发送 showQuestion 命令,避免重复显示
// 创建 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
});
// 设置超时2小时
setTimeout(() => {
if (this.pendingQuestions.has(askId)) {
this.pendingQuestions.delete(askId);
reject(new Error('用户回答超时'));
}
}, 7200000);
});
}
/**
* 处理用户提交的回答(从 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> {
// 检查是否是工具确认类型的问题
if (askId.startsWith('tool_confirm_')) {
// 提取 confirmId
const confirmId = parseInt(askId.replace('tool_confirm_', ''));
const approved = answer === '确认执行';
console.log(`[UserInteraction] 提交工具确认: confirmId=${confirmId}, approved=${approved}`);
try {
const response = await submitToolConfirm({
confirmId,
taskId,
approved
});
if (!response.success) {
throw new Error(response.error || '提交工具确认失败');
}
console.log(`[UserInteraction] 工具确认已提交: confirmId=${confirmId}`);
} catch (error) {
console.error(`[UserInteraction] 提交工具确认失败: confirmId=${confirmId}`, error);
throw error;
}
} else {
// 普通问题回答
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();