refactor(core,server): 统一模块职责并消除类型重复

Core 模块职责:
- 添加 inferPermissionType() 权限类型推断函数
- 添加 partToApiFormat()/partsToApiFormat() API 格式转换函数
- 添加 ApiPart/ApiTextPart/ApiToolPart/ApiReasoningPart 类型
- 统一导出 toolRegistry 供 Server 使用

Server 模块职责:
- 重命名 PermissionRequestContext 为 PermissionDisplayContext
- 移除本地 toolRegistry,直接使用 Core 的注册表
- 使用 Core 的 inferPermissionType 替代本地实现
- 使用 Core 的 partsToApiFormat 替代手动转换

文档更新:
- 在 gui-server-client.md 添加第11章「模块职责边界」
- 明确 Core 和 Server 的职责划分
This commit is contained in:
2025-12-16 21:28:19 +08:00
parent 0a26c3ab72
commit e53035ffc0
12 changed files with 274 additions and 185 deletions
+8 -38
View File
@@ -7,9 +7,10 @@ import { randomUUID } from 'crypto';
import { broadcastToSession } from '../ws.js';
import type {
PermissionRequestPayload,
PermissionRequestContext,
PermissionDisplayContext,
ServerMessage,
} from '../types.js';
import { inferPermissionType } from '@ai-assistant/core';
import type { PermissionDecision, PermissionContext, PermissionType } from '@ai-assistant/core';
// 等待中的权限请求
@@ -79,42 +80,11 @@ function isAutoApproved(sessionId: string, ctx: PermissionContext): boolean {
}
/**
* 从上下文获取权限类型
* 优先使用结构化的 permissionType 字段,否则解析 command 字符串
* 构建权限请求显示上下文
* 将 Core 的完整 PermissionContext 转换为用于前端显示的精简格式
*/
function getPermissionType(ctx: PermissionContext): PermissionType {
// 优先使用结构化字段
if (ctx.permissionType) {
return ctx.permissionType;
}
// 向后兼容:解析 command 字符串
const command = ctx.command.toLowerCase();
if (command.startsWith('git ')) {
return 'git';
}
const fileOps = ['read', 'write', 'edit', 'delete', 'move', 'copy', 'mkdir'];
for (const op of fileOps) {
if (command.startsWith(`${op} `)) {
return 'file';
}
}
if (command.includes('fetch') || command.includes('http') || command.startsWith('web_search')) {
return 'web';
}
return 'bash';
}
/**
* 构建权限请求上下文
* 使用 Core 传递的结构化字段,减少字符串解析
*/
function buildRequestContext(ctx: PermissionContext): PermissionRequestContext {
const permType = getPermissionType(ctx);
function buildDisplayContext(ctx: PermissionContext): PermissionDisplayContext {
const permType = inferPermissionType(ctx);
switch (permType) {
case 'file':
@@ -162,8 +132,8 @@ export function createServerPermissionCallback(sessionId: string) {
}
const requestId = randomUUID();
const permissionType = getPermissionType(permCtx);
const context = buildRequestContext(permCtx);
const permissionType = inferPermissionType(permCtx);
const context = buildDisplayContext(permCtx);
// 构建请求 payload
const payload: PermissionRequestPayload = {