feat: 升级至 v0.2.1 — 流式渲染修复、安全增强、工具自动执行
流式渲染修复: - runId 机制防止 abort 后旧流事件污染新 run - run lock 防止并发 run 污染引擎状态 - abort race 提前退出工具执行等待 - TERMINATED 状态通过 stateChange 发射 - tool_call_delta 流式参数拼接 + pending 占位替换 - 首轮卡片创建路径统一,traceStep 按 ID 精确匹配 - compressed 事件转发为 toast 通知 安全增强: - ConfirmationHook 支持持久化自动执行(跨会话) - 设置面板新增自动执行工具管理 UI - SandboxManager 双重安全校验 fail-closed - 审计日志链式哈希防篡改 - PromptInjectionDefender 中文注入标记清理 - scanCode 28 模式 + base64/$() 检测 - validatePath realpathSync 防符号链接逃逸 - code-search 使用 execFile 防命令注入 新增工具: - file_editor、code_search、task_manager、diff_viewer 其他: - Agent Loop 加 PARSING/REFLECTING 状态 + 指数退避重试 - MemoryManager TF-IDF 语义检索 - run_command Windows 中文编码修复(chcp 65001) - 版本号 0.2.0 → 0.2.1
This commit is contained in:
+70
-11
@@ -30,6 +30,7 @@ import { WindowManager } from './services/window-manager.service';
|
||||
import { MCPManager } from './services/mcp-manager.service';
|
||||
import { ContextBuilder } from './harness/prompts/context-builder';
|
||||
import { MemoryManager } from './harness/memory/manager';
|
||||
import { MemoryConsolidator } from './harness/memory/consolidator';
|
||||
import { registerAllIPCHandlers } from './ipc/handlers';
|
||||
import { AgentLoopEngine } from './harness/agent-loop';
|
||||
import { ToolRegistry } from './harness/tools/registry';
|
||||
@@ -43,8 +44,14 @@ import {
|
||||
RunCommandTool,
|
||||
WebBrowserTool, cleanupBrowser,
|
||||
DelegateTaskTool,
|
||||
// v0.2.0 新增工具
|
||||
FileEditorTool,
|
||||
CodeSearchTool,
|
||||
TaskManagerTool,
|
||||
DiffViewerTool,
|
||||
} from './harness/tools/built-in';
|
||||
import { AuditLogHook, MemoryTriggerHook, PermissionCheckHook, RateLimitHook } from './harness/hooks';
|
||||
import { ConfirmationHook } from './harness/hooks/confirmation-hook';
|
||||
import { PolicyEngine } from './harness/sandbox/permissions';
|
||||
import { SandboxManager } from './harness/sandbox/sandbox';
|
||||
import { PromptInjectionDefender } from './harness/security/prompt-injection-defense';
|
||||
@@ -150,6 +157,19 @@ async function initialize(): Promise<void> {
|
||||
// ===== 步骤 5: 工作空间文件 + System Prompt =====
|
||||
const contextBuilder = new ContextBuilder();
|
||||
|
||||
// ===== v0.2.0: 安全模块(必须在工具注册之前,便于 RunCommandTool 注入 SandboxManager)=====
|
||||
const policyEngine = new PolicyEngine();
|
||||
const sandboxManager = new SandboxManager({
|
||||
allowedPaths: [workspaceInfo.path],
|
||||
networkPolicy: 'allowlist',
|
||||
});
|
||||
const promptDefender = new PromptInjectionDefender();
|
||||
const outputValidator = new OutputValidator();
|
||||
|
||||
// v0.2.0: ConfirmationHook(提前创建,mainWindow 创建后再注入)
|
||||
// 注入 ConfigService 以支持持久化自动执行设置
|
||||
const confirmationHook = new ConfirmationHook(null, configService);
|
||||
|
||||
// ===== 步骤 6: 注册内置工具 =====
|
||||
const toolRegistry = new ToolRegistry();
|
||||
toolRegistry.registerBuiltin(new ReadFileTool());
|
||||
@@ -157,6 +177,11 @@ async function initialize(): Promise<void> {
|
||||
toolRegistry.registerBuiltin(new ListDirectoryTool());
|
||||
toolRegistry.registerBuiltin(new SearchFilesTool());
|
||||
|
||||
// v0.2.0: 新增文件工具
|
||||
toolRegistry.registerBuiltin(new FileEditorTool());
|
||||
toolRegistry.registerBuiltin(new CodeSearchTool());
|
||||
toolRegistry.registerBuiltin(new DiffViewerTool());
|
||||
|
||||
// WebFetchTool 先于 WebSearchTool 构造,注入为依赖
|
||||
const webFetchTool = new WebFetchTool();
|
||||
toolRegistry.registerBuiltin(webFetchTool);
|
||||
@@ -164,7 +189,14 @@ async function initialize(): Promise<void> {
|
||||
|
||||
toolRegistry.registerBuiltin(new MemoryStoreTool(memoryManager));
|
||||
toolRegistry.registerBuiltin(new MemorySearchTool(memoryManager));
|
||||
toolRegistry.registerBuiltin(new RunCommandTool());
|
||||
|
||||
// v0.2.0: RunCommandTool 注入 SandboxManager
|
||||
const runCommandTool = new RunCommandTool();
|
||||
runCommandTool.setSandboxManager(sandboxManager);
|
||||
toolRegistry.registerBuiltin(runCommandTool);
|
||||
|
||||
// v0.2.0: 任务管理工具
|
||||
toolRegistry.registerBuiltin(new TaskManagerTool(() => db));
|
||||
|
||||
// 注册 Web Browser 统一浏览器工具
|
||||
toolRegistry.registerBuiltin(new WebBrowserTool());
|
||||
@@ -176,19 +208,13 @@ async function initialize(): Promise<void> {
|
||||
log.warn('MCP Manager initialization error:', err);
|
||||
});
|
||||
|
||||
// ===== 安全模块 =====
|
||||
const policyEngine = new PolicyEngine();
|
||||
const sandboxManager = new SandboxManager({
|
||||
allowedPaths: [workspaceInfo.path],
|
||||
networkPolicy: 'allowlist',
|
||||
});
|
||||
const promptDefender = new PromptInjectionDefender();
|
||||
const outputValidator = new OutputValidator();
|
||||
|
||||
// ===== Hooks =====
|
||||
// v0.2.0: ConfirmationHook 注入到 preToolHooks 管道
|
||||
// 注意:setToolDefs 延迟到 DelegateTaskTool 注册后调用,确保包含所有工具的风险等级
|
||||
const preToolHooks = [
|
||||
new PermissionCheckHook(policyEngine),
|
||||
new RateLimitHook(20),
|
||||
confirmationHook,
|
||||
];
|
||||
const postToolHooks = [
|
||||
new AuditLogHook(auditService),
|
||||
@@ -215,6 +241,9 @@ async function initialize(): Promise<void> {
|
||||
agentLoop.setTools(toolRegistry.listTools());
|
||||
agentLoop.setWorkspacePath(workspaceInfo.path);
|
||||
|
||||
// ===== Memory Consolidator(会话结束 AI 提取重要记忆到 MEMORY.md)=====
|
||||
const memoryConsolidator = new MemoryConsolidator(adapter, workspaceService);
|
||||
|
||||
// ===== Task Orchestrator(子任务委派)=====
|
||||
const orchestrator = new TaskOrchestrator(
|
||||
agentLoop, toolRegistry, preToolHooks, postToolHooks,
|
||||
@@ -227,12 +256,16 @@ async function initialize(): Promise<void> {
|
||||
toolRegistry.registerBuiltin(new DelegateTaskTool(orchestrator));
|
||||
// 重新设置工具列表,包含新注册的 delegate_task
|
||||
agentLoop.setTools(toolRegistry.listTools());
|
||||
// v0.2.0: 在所有工具(包括 DelegateTaskTool)注册完成后,刷新 ConfirmationHook 的工具定义缓存
|
||||
confirmationHook.setToolDefs(toolRegistry.listAllTools());
|
||||
|
||||
// ===== 热重载 Adapter 回调(设置变更时触发)=====
|
||||
let lastProvider = configService.get<string>('llm.provider') ?? '';
|
||||
const reloadAdapter = () => {
|
||||
try {
|
||||
const newAdapter = createAdapter();
|
||||
agentLoop.setAdapter(newAdapter);
|
||||
memoryConsolidator.setAdapter(newAdapter);
|
||||
// Provider 切换时同步 contextLength:仅 Ollama 使用 numCtx 作为有效上下文窗口
|
||||
const provider = configService.get<string>('llm.provider') ?? '';
|
||||
if (provider === 'ollama') {
|
||||
@@ -242,8 +275,30 @@ async function initialize(): Promise<void> {
|
||||
agentLoop.updateConfig({ contextLength: undefined });
|
||||
}
|
||||
log.info(`[CONFIG] Adapter reloaded: provider=${provider}`);
|
||||
// 通知渲染进程 Provider 已切换(UI 显示 Toast + 系统消息)
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
if (lastProvider && lastProvider !== provider) {
|
||||
mainWindow.webContents.send('agent:providerSwitched', {
|
||||
from: lastProvider,
|
||||
to: provider,
|
||||
reason: 'config_changed',
|
||||
});
|
||||
}
|
||||
mainWindow.webContents.send('toast:show', {
|
||||
type: 'success',
|
||||
message: `Provider 已切换: ${lastProvider || '未知'} → ${provider}`,
|
||||
});
|
||||
}
|
||||
lastProvider = provider;
|
||||
} catch (err) {
|
||||
log.error(`[CONFIG] Failed to reload adapter: ${(err as Error).message}`);
|
||||
// 通知渲染进程 Provider 切换失败(UI 显示错误 Toast)
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send('toast:show', {
|
||||
type: 'error',
|
||||
message: `Provider 切换失败: ${(err as Error).message}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -255,6 +310,9 @@ async function initialize(): Promise<void> {
|
||||
title: 'MetonaAI Desktop',
|
||||
});
|
||||
|
||||
// v0.2.0: 为 ConfirmationHook 注入主窗口(用于向渲染进程发送确认请求)
|
||||
confirmationHook.setMainWindow(mainWindow);
|
||||
|
||||
// ===== 系统托盘 =====
|
||||
const resourcesPath = join(__dirname, '../../assets');
|
||||
trayManager = new TrayManager(resourcesPath);
|
||||
@@ -267,7 +325,7 @@ async function initialize(): Promise<void> {
|
||||
agentLoop.on('stateChange', (data: { previous: string; current: string; state?: string }) => {
|
||||
const statusMap: Record<string, 'idle' | 'thinking' | 'executing' | 'error'> = {
|
||||
INIT: 'idle', THINKING: 'thinking', PARSING: 'thinking',
|
||||
EXECUTING: 'executing', OBSERVING: 'executing', REFLECTING: 'thinking',
|
||||
EXECUTING: 'executing', OBSERVING: 'thinking', REFLECTING: 'thinking',
|
||||
COMPRESSING: 'thinking', TERMINATED: 'idle',
|
||||
};
|
||||
const stateValue = (data.current || data.state) ?? '';
|
||||
@@ -289,6 +347,7 @@ async function initialize(): Promise<void> {
|
||||
contextBuilder, agentLoop, toolRegistry, auditService,
|
||||
sessionRecorder, memoryManager, mcpManager, createAdapter,
|
||||
promptDefender, outputValidator,
|
||||
confirmationHook, memoryConsolidator,
|
||||
);
|
||||
|
||||
// TODO: Initialize UpdateService for auto-update functionality
|
||||
|
||||
Reference in New Issue
Block a user