feat(ui): 实现 Agent 模式切换和 Auto Edit 功能

- 添加 AgentModeSelector 组件,支持 Build/Plan 模式切换
- Build 模式下显示 Auto Edit 开关,自动授权文件写入/编辑
- 扩展 useChat hook 添加会话级别的 agentMode/autoApprove 状态
- 服务端支持解析和应用 Agent 模式配置
- 权限处理器实现 auto-approve 检查(仅 write/edit,不含 delete)
This commit is contained in:
2025-12-15 19:42:51 +08:00
parent f09f8f2b03
commit ec3c7bccf9
13 changed files with 409 additions and 7 deletions
+54
View File
@@ -39,9 +39,56 @@ interface PendingRequest {
const pendingRequests = new Map<string, PendingRequest>();
// 会话级别的 auto-approve 配置
// key: sessionId, value: auto-approve 配置
const sessionAutoApprove = new Map<string, { file?: { write?: 'allow'; edit?: 'allow' } }>();
// 默认超时时间(60秒)
const PERMISSION_TIMEOUT = 60000;
/**
* 设置会话的 auto-approve 配置
*/
export function setSessionAutoApprove(
sessionId: string,
config: { file?: { write?: 'allow'; edit?: 'allow' } } | null
): void {
if (config) {
sessionAutoApprove.set(sessionId, config);
} else {
sessionAutoApprove.delete(sessionId);
}
}
/**
* 获取会话的 auto-approve 配置
*/
export function getSessionAutoApprove(sessionId: string): { file?: { write?: 'allow'; edit?: 'allow' } } | null {
return sessionAutoApprove.get(sessionId) ?? null;
}
/**
* 检查操作是否被 auto-approve
*/
function isAutoApproved(sessionId: string, ctx: PermissionContext): boolean {
const config = sessionAutoApprove.get(sessionId);
if (!config) return false;
const command = ctx.command.toLowerCase();
// 检查是否为文件写入操作
if (command.startsWith('write ') && config.file?.write === 'allow') {
return true;
}
// 检查是否为文件编辑操作
if (command.startsWith('edit ') && config.file?.edit === 'allow') {
return true;
}
return false;
}
/**
* 从命令或上下文检测权限类型
*/
@@ -117,6 +164,13 @@ function buildRequestContext(ctx: PermissionContext): PermissionRequestContext {
export function createServerPermissionCallback(sessionId: string) {
return async (ctx: unknown): Promise<PermissionDecision> => {
const permCtx = ctx as PermissionContext;
// 检查 auto-approve 配置
if (isAutoApproved(sessionId, permCtx)) {
console.log(`[Permission] Auto-approved: ${permCtx.command}`);
return { allow: true, remember: false };
}
const requestId = randomUUID();
const permissionType = detectPermissionType(permCtx);
const context = buildRequestContext(permCtx);