feat: 重构为 Monorepo 架构并实现 HTTP Server

架构变更:
- 采用 pnpm workspaces 实现 Monorepo 结构
- 将现有代码迁移到 packages/core
- 新增 packages/server HTTP 服务层

Server 功能:
- REST API: 会话管理、工具管理、配置管理
- WebSocket: 实时双向通信支持
- SSE: 服务端事件推送
- Hono + Bun 作为运行时

API 端点:
- GET/POST /api/sessions - 会话 CRUD
- GET/POST /api/sessions/:id/messages - 消息管理
- GET /api/sessions/:id/events - SSE 事件流
- WS /api/ws/:sessionId - WebSocket 连接
- GET/POST /api/tools - 工具管理
- GET/PUT /api/config - 配置管理
This commit is contained in:
2025-12-12 10:42:20 +08:00
parent 59dbed926e
commit 5e32375f0e
301 changed files with 3281 additions and 43 deletions
+322
View File
@@ -0,0 +1,322 @@
import {
generateText,
streamText,
stepCountIs,
type ModelMessage,
type Tool as AITool,
type LanguageModel,
} from 'ai';
import type { Tool, ToolResult, AgentConfig, ContentBlock } from '../types/index.js';
import { buildZodSchema } from '../types/index.js';
import { ToolRegistry } from '../tools/registry.js';
import type {
AgentInfo,
AgentExecutionContext,
AgentExecutionResult,
ImageData,
} from './types.js';
import { checkBashPermission } from './permission-merger.js';
import { getModelFactory } from '../core/providers.js';
/**
* Agent 执行器
* 根据 Agent 配置执行任务,支持工具过滤和权限控制
*/
export class AgentExecutor {
private agentInfo: AgentInfo;
private baseConfig: AgentConfig;
private toolRegistry: ToolRegistry;
private getModel: (model: string) => LanguageModel;
constructor(
agentInfo: AgentInfo,
baseConfig: AgentConfig,
toolRegistry: ToolRegistry
) {
this.agentInfo = agentInfo;
this.baseConfig = baseConfig;
this.toolRegistry = toolRegistry;
// 获取模型工厂
const provider = agentInfo.model?.provider ?? baseConfig.provider;
this.getModel = getModelFactory(provider, {
apiKey: baseConfig.apiKey,
baseUrl: baseConfig.baseUrl,
});
}
/**
* 执行任务
*/
async execute(
prompt: string,
context: AgentExecutionContext
): Promise<AgentExecutionResult> {
const { onStream, onToolCall, onToolResult, images } = context;
// 获取过滤后的工具
const tools = this.getFilteredTools();
const vercelTools = this.buildVercelTools(tools);
// 构建系统提示词
const systemPrompt = this.buildSystemPrompt();
// 获取模型配置
const modelName = this.agentInfo.model?.model ?? this.baseConfig.model;
const maxSteps = this.agentInfo.maxSteps ?? 10;
const maxTokens = this.agentInfo.model?.maxTokens ?? this.baseConfig.maxTokens;
// 构建消息内容(支持图片)
const messageContent = this.buildMessageContent(prompt, images);
// 构建初始消息
const messages: ModelMessage[] = [
{
role: 'user',
content: messageContent,
},
];
let fullResponse = '';
let steps = 0;
try {
if (onStream) {
// 流式模式
const result = streamText({
model: this.getModel(modelName),
system: systemPrompt,
messages,
tools: vercelTools,
maxOutputTokens: maxTokens,
stopWhen: stepCountIs(maxSteps),
onChunk: ({ chunk }) => {
if (chunk.type === 'tool-call') {
steps++;
const toolArgs = 'input' in chunk ? chunk.input : {};
onToolCall?.(chunk.toolName, toolArgs as Record<string, unknown>);
onStream(`\n[调用工具: ${chunk.toolName}]\n`);
} else if (chunk.type === 'tool-result') {
const output = (chunk as { output?: ToolResult }).output;
onToolResult?.(
(chunk as { toolName?: string }).toolName ?? 'unknown',
output
);
if (output && typeof output === 'object') {
if (output.success) {
const displayOutput =
output.output.length > 500
? output.output.substring(0, 500) + '...(截断)'
: output.output;
onStream(`[结果: ${displayOutput}]\n`);
} else {
onStream(`[错误: ${output.error}]\n`);
}
}
}
},
});
for await (const chunk of result.textStream) {
fullResponse += chunk;
onStream(chunk);
}
await result.response;
} else {
// 非流式模式
const result = await generateText({
model: this.getModel(modelName),
system: systemPrompt,
messages,
tools: vercelTools,
maxOutputTokens: maxTokens,
stopWhen: stepCountIs(maxSteps),
});
fullResponse = result.text;
steps = result.steps.length;
}
return {
success: true,
text: fullResponse,
steps,
sessionId: context.parentSessionId ?? 'standalone',
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
success: false,
text: '',
steps,
sessionId: context.parentSessionId ?? 'standalone',
error: errorMessage,
};
}
}
/**
* 获取过滤后的工具列表
*/
private getFilteredTools(): Tool[] {
const allTools = this.toolRegistry.getAllTools();
const toolConfig = this.agentInfo.tools;
// 如果没有工具配置,返回所有工具
if (!toolConfig) {
return allTools;
}
let filteredTools = allTools;
// 如果指定了 enabled,只保留这些工具
if (toolConfig.enabled && toolConfig.enabled.length > 0) {
const enabledSet = new Set(toolConfig.enabled);
filteredTools = filteredTools.filter((t) => enabledSet.has(t.name));
}
// 移除 disabled 的工具
if (toolConfig.disabled && toolConfig.disabled.length > 0) {
const disabledSet = new Set(toolConfig.disabled);
filteredTools = filteredTools.filter((t) => !disabledSet.has(t.name));
}
// 如果禁止嵌套 Task,移除 task 工具
if (toolConfig.noTask) {
filteredTools = filteredTools.filter((t) => t.name !== 'task');
}
return filteredTools;
}
/**
* 构建 Vercel AI SDK 工具格式
*/
private buildVercelTools(tools: Tool[]): Record<string, AITool> {
const vercelTools: Record<string, AITool> = {};
for (const tool of tools) {
const schema = buildZodSchema(tool.parameters);
vercelTools[tool.name] = {
description: tool.description,
inputSchema: schema,
execute: async (params) => {
// 权限检查
const permissionResult = await this.checkToolPermission(
tool.name,
params as Record<string, unknown>
);
if (!permissionResult.allowed) {
return {
success: false,
output: '',
error: `权限拒绝: ${permissionResult.reason}`,
};
}
return tool.execute(params as Record<string, unknown>);
},
} as AITool;
}
return vercelTools;
}
/**
* 检查工具调用权限
*/
private async checkToolPermission(
toolName: string,
params: Record<string, unknown>
): Promise<{ allowed: boolean; reason?: string }> {
const permission = this.agentInfo.permission;
if (!permission) {
return { allowed: true };
}
// Bash 权限检查
if (toolName === 'bash' && permission.bash) {
const command = params.command as string;
if (!command) {
return { allowed: true };
}
const action = checkBashPermission(command, permission.bash);
if (action === 'deny') {
return { allowed: false, reason: `命令被禁止: ${command}` };
}
// ask 在这里视为允许(实际的 ask 逻辑在权限管理器中处理)
}
// 文件写入权限检查
if (['write_file', 'edit_file', 'delete_file'].includes(toolName)) {
const filePermission = permission.file;
if (filePermission) {
const operation = toolName === 'write_file' ? 'write' :
toolName === 'edit_file' ? 'edit' : 'delete';
const action = filePermission[operation];
if (action === 'deny') {
return { allowed: false, reason: `${operation} 操作被禁止` };
}
}
}
// Git 写操作权限检查
const gitWriteTools = ['git_add', 'git_commit', 'git_push', 'git_checkout', 'git_stash'];
if (gitWriteTools.includes(toolName) && permission.git?.write === 'deny') {
return { allowed: false, reason: 'Git 写操作被禁止' };
}
return { allowed: true };
}
/**
* 构建系统提示词
*/
private buildSystemPrompt(): string {
// 如果 Agent 有自定义 prompt,使用它
if (this.agentInfo.prompt) {
return this.agentInfo.prompt;
}
// 否则使用基础配置的 systemPrompt
return this.baseConfig.systemPrompt;
}
/**
* 构建消息内容(支持图片)
*/
private buildMessageContent(
prompt: string,
images?: ImageData[]
): string | ContentBlock[] {
// 如果没有图片,直接返回文本
if (!images || images.length === 0) {
return prompt;
}
// 构建多模态内容
const blocks: ContentBlock[] = [];
// 先添加图片
for (const img of images) {
blocks.push({
type: 'image',
image: img.data,
mimeType: img.mimeType,
});
}
// 再添加文本
if (prompt) {
blocks.push({
type: 'text',
text: prompt,
});
}
return blocks;
}
}