refactor: 将 Ask 模式的工具确认从弹窗改为内嵌聊天卡片

## 主要修改

   ### dialogService.ts
   - 移除 `vscode.window.showWarningMessage` 弹窗
   - 将工具确认改为添加 question 类型的 segment
   - 使用 `userInteractionManager.handleAskUser` 等待用户回答
   - 生成唯一的 askId: `tool_confirm_{confirmId}`

   ### userInteraction.ts
   - 导入 `submitToolConfirm` 方法
   - 在 `submitUserAnswer` 中识别工具确认类型的 askId
   - 根据用户选择("确认执行" / "取消")调用对应的 API

   ## 用户体验改进
   - 工具确认问题自然融入对话流程
   - 用户可以看到历史确认记录
   - 非阻塞式交互,体验更流畅
This commit is contained in:
Roe-xin
2025-12-31 10:18:35 +08:00
parent 1ce1ed715c
commit b662d25c9c
2 changed files with 93 additions and 34 deletions

View File

@ -301,30 +301,64 @@ export class DialogSession {
onToolConfirm: async (data: ToolConfirmEvent) => { onToolConfirm: async (data: ToolConfirmEvent) => {
console.log('[DialogSession] onToolConfirm:', data.toolName, data.confirmId); console.log('[DialogSession] onToolConfirm:', data.toolName, data.confirmId);
// 调用回调通知 UI 显示确认对话框 // 结束当前文本段落
this.finalizeTextSegment();
// 生成工具描述
const toolDescription = this.getToolDescription(data.toolName, data.toolInput);
// 构建问题文本
const toolNameMap: Record<string, string> = {
'file_write': '写入文件',
'file_delete': '删除文件',
'syntax_check': '语法检查',
'simulation': '运行仿真'
};
const toolDisplayName = toolNameMap[data.toolName] || data.toolName;
const question = `确认执行操作:${toolDisplayName}\n\n${toolDescription}`;
// 生成唯一的 askId
const askId = `tool_confirm_${data.confirmId}`;
// 添加问题段落到聊天界面
this.segments.push({
type: 'question',
askId: askId,
question: question,
options: ['确认执行', '取消']
});
// 实时发送段落更新
callbacks.onSegmentUpdate?.(this.segments);
// 调用回调通知 UI
callbacks.onToolConfirm?.(data.confirmId, data.toolName, data.toolInput); callbacks.onToolConfirm?.(data.confirmId, data.toolName, data.toolInput);
// 使用 VSCode 快速选择框显示确认对话框 // 使用 userInteractionManager 等待用户回答
const toolDescription = this.getToolDescription(data.toolName, data.toolInput); try {
const result = await vscode.window.showWarningMessage( await userInteractionManager.handleAskUser(
`确认执行操作: ${data.toolName}`, {
{ modal: true, detail: toolDescription }, askId: askId,
'确认执行', question: question,
'取消' options: ['确认执行', '取消']
} as AskUserEvent,
this.taskId
); );
const approved = result === '确认执行'; // 注意:用户回答后,需要在 receiveAnswer 中处理 tool_confirm 类型的 askId
console.log('[DialogSession] 用户确认结果:', approved); // 这里不直接调用 submitToolConfirm而是在 userInteractionManager 中统一处理
} catch (error) {
// 发送确认响应到后端 console.error('[DialogSession] 处理工具确认失败:', error);
// 如果出错,默认取消执行
try { try {
await submitToolConfirm({ await submitToolConfirm({
confirmId: data.confirmId, confirmId: data.confirmId,
taskId: this.taskId, taskId: this.taskId,
approved approved: false
}); });
} catch (error) { } catch (submitError) {
console.error('[DialogSession] 发送确认响应失败:', error); console.error('[DialogSession] 发送取消响应失败:', submitError);
}
} }
}, },

View File

@ -3,7 +3,7 @@
* 处理 ask_user 事件,通过 WebView 显示问题并收集用户回答 * 处理 ask_user 事件,通过 WebView 显示问题并收集用户回答
*/ */
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { submitAnswer } from './apiClient'; import { submitAnswer, submitToolConfirm } from './apiClient';
import type { AskUserEvent, AnswerRequest } from '../types/api'; import type { AskUserEvent, AnswerRequest } from '../types/api';
/** /**
@ -107,6 +107,30 @@ export class UserInteractionManager {
taskId: string, taskId: string,
answer: string answer: string
): Promise<void> { ): 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 = { const request: AnswerRequest = {
askId, askId,
taskId, taskId,
@ -124,6 +148,7 @@ export class UserInteractionManager {
throw error; throw error;
} }
} }
}
/** /**
* 取消所有待处理的问题 * 取消所有待处理的问题