feat: 升级至 v0.3.7 — 前后端状态同步与错误处理全量修复
核心引擎修复: - CE-1: 上下文压缩摘要 role 从 system 改为 user,避免被 adapter 过滤 - CE-2: 工具失败时优先使用 error 字段(engine/openai-format/ollama 三处) - P0-1: DeadLoopError 终止时正确传 error 参数,前端可见 ERROR 事件 - MT-1: 新增 waitForAbort 方法,abortSession 等待 run 结束再返回 - MT-2: TERMINATED 状态到达时标记步骤完成,避免 Trace Viewer 转圈 - MT-3: 压缩边界检测孤立 tool 消息,避免 API 400 错误 - isRetryableError 与 catch 分支统一 toLowerCase IPC 与主进程修复: - P0-2: createAdapter 配置缺失返回 null,FALLBACK_ADAPTER 兜底 - P0-3: reloadAdapter 失败返回 success:false 通知前端 - P1-5: 校验失败发 ERROR+DONE 流事件,防止 isStreaming 卡死 - P1-6: configLoaded 标志,配置加载前禁用发送按钮 - P1-7: MCP initialize 移到 agentLoop 后,完成后同步工具 - P2-11: beforeLoad 在 loadURL 前注册 IPC handler - P2-12: provider 切换竞态保护 前端状态同步修复: - clearSessions 后同步清空前端会话与消息状态 - clearMemories 通过 memoryVersion 触发 MemoryViewer 重新加载 - ContextMenu 4 个 session 操作补全 IPC 调用与 try/catch - useConfig 配置保存失败回滚 UI 并提示 - handleToggle 工具切换失败回滚单个工具状态 错误处理全量补全: - 所有 await window.metona 调用补全 try/catch 与 toast 反馈 - MCP addServer/toggleServer/removeServer 检查返回值 - showItemInFolder 检查返回值(handleOpen/handleOpenInFolder) - sse-stream/ollama NDJSON 解析失败改为 log.warn - adapter throwHttpError 带 status 属性供 isRetryableError 判断
This commit is contained in:
+47
-18
@@ -38,6 +38,7 @@ import { DeepSeekAdapter } from './harness/adapters/deepseek.adapter';
|
||||
import { AgnesAdapter } from './harness/adapters/agnes-ai.adapter';
|
||||
import { MimoAdapter } from './harness/adapters/mimo.adapter';
|
||||
import { OllamaAdapter } from './harness/adapters/ollama.adapter';
|
||||
import type { IMetonaProviderAdapter } from './harness/types/metona-adapter';
|
||||
import {
|
||||
ReadFileTool, WriteFileTool, ListDirectoryTool, SearchFilesTool,
|
||||
WebSearchTool, WebFetchTool,
|
||||
@@ -140,18 +141,22 @@ async function initialize(): Promise<void> {
|
||||
memoryManager.initialize();
|
||||
|
||||
// ===== 步骤 4: Provider Adapter 工厂 =====
|
||||
const createAdapter = () => {
|
||||
// P0-2 修复: 配置缺失时返回 null 而非抛异常,让应用能启动到 Onboarding
|
||||
const createAdapter = (): IMetonaProviderAdapter | null => {
|
||||
const provider = configService.get<string>('llm.provider') ?? '';
|
||||
const model = configService.get<string>('llm.model') ?? '';
|
||||
const apiKey = configService.get<string>('llm.apiKey') ?? '';
|
||||
const baseURL = configService.get<string>('llm.baseURL') ?? '';
|
||||
|
||||
// 配置不完整时返回 null(不抛异常,让应用能启动到 Onboarding)
|
||||
if (!provider || !baseURL || !model) {
|
||||
log.warn('LLM not configured. Please set provider, baseURL, and model in Settings.');
|
||||
log.warn('LLM not fully configured. Please set provider, baseURL, and model in Settings.');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!apiKey && provider !== 'ollama') {
|
||||
throw new Error(`API key is required for provider "${provider || 'unknown'}". Please set it in Settings.`);
|
||||
log.warn(`API key is required for provider "${provider}". Please set it in Settings.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// v0.3.1: 读取 Provider 对应的 contextWindow 配置(Ollama 不使用此字段)
|
||||
@@ -168,7 +173,12 @@ async function initialize(): Promise<void> {
|
||||
}
|
||||
};
|
||||
|
||||
const adapter = createAdapter();
|
||||
// 配置未就绪时的 fallback adapter — getContextWindow 返回安全值,send/sendStream 会报错但 P0-1 修复后前端可见
|
||||
const FALLBACK_ADAPTER = new DeepSeekAdapter({
|
||||
provider: '', baseURL: '', apiKey: '', defaultModel: '', contextWindow: 4096,
|
||||
});
|
||||
|
||||
const adapter = createAdapter() ?? FALLBACK_ADAPTER;
|
||||
|
||||
// ===== 步骤 5: 工作空间文件 + System Prompt =====
|
||||
const contextBuilder = new ContextBuilder();
|
||||
@@ -242,9 +252,7 @@ async function initialize(): Promise<void> {
|
||||
|
||||
// ===== MCP Manager =====
|
||||
const mcpManager = new MCPManager(() => db, toolRegistry);
|
||||
mcpManager.initialize().catch((err) => {
|
||||
log.warn('MCP Manager initialization error:', err);
|
||||
});
|
||||
// P1-7 修复: initialize() 移到 agentLoop 创建之后,完成后重新同步工具
|
||||
|
||||
// ===== Hooks =====
|
||||
// v0.2.0: ConfirmationHook 注入到 preToolHooks 管道
|
||||
@@ -285,6 +293,14 @@ async function initialize(): Promise<void> {
|
||||
agentLoop.setTools(toolRegistry.listTools());
|
||||
agentLoop.setWorkspacePath(workspaceInfo.path);
|
||||
|
||||
// P1-7 修复: MCP 初始化在 agentLoop 创建后执行,完成后重新同步工具到 AgentLoop
|
||||
mcpManager.initialize().then(() => {
|
||||
agentLoop.setTools(toolRegistry.listTools());
|
||||
log.info('[MCP] Tools registered and synced to AgentLoop');
|
||||
}).catch((err) => {
|
||||
log.warn('MCP Manager initialization error:', err);
|
||||
});
|
||||
|
||||
// ===== Memory Consolidator(会话结束 AI 提取重要记忆到 MEMORY.md)=====
|
||||
const memoryConsolidator = new MemoryConsolidator(adapter, workspaceService);
|
||||
|
||||
@@ -343,6 +359,17 @@ async function initialize(): Promise<void> {
|
||||
|
||||
// 配置变化 — 重建 adapter
|
||||
const newAdapter = createAdapter();
|
||||
if (!newAdapter) {
|
||||
log.warn('[CONFIG] Cannot create adapter: LLM config incomplete (provider/apiKey/baseURL/model)');
|
||||
// 不更新 lastConfigSig,下次还会尝试
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send('toast:show', {
|
||||
type: 'warning',
|
||||
message: 'LLM 配置不完整,请在设置中补全 Provider、API Key、Base URL 和 Model',
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
agentLoop.setAdapter(newAdapter);
|
||||
memoryConsolidator.setAdapter(newAdapter);
|
||||
// Provider 切换时同步 contextLength 和 contextWindow
|
||||
@@ -399,10 +426,21 @@ async function initialize(): Promise<void> {
|
||||
id: 'main',
|
||||
workspacePath: workspaceInfo.path,
|
||||
title: 'MetonaAI Desktop',
|
||||
// P2-11 修复: 在 loadURL 之前注册 IPC handler,消除渲染进程加载与 IPC 注册的时序窗口
|
||||
beforeLoad: (win) => {
|
||||
confirmationHook.setMainWindow(win);
|
||||
registerAllIPCHandlers(
|
||||
win, sessionService, configService, workspaceService,
|
||||
contextBuilder, agentLoop, toolRegistry, auditService,
|
||||
sessionRecorder, memoryManager, mcpManager, reloadAdapter,
|
||||
promptDefender, outputValidator,
|
||||
confirmationHook, memoryConsolidator, orchestrator,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// v0.2.0: 为 ConfirmationHook 注入主窗口(用于向渲染进程发送确认请求)
|
||||
confirmationHook.setMainWindow(mainWindow);
|
||||
// P2-11 修复: ConfirmationHook.setMainWindow 已在 beforeLoad 中调用,此处无需重复
|
||||
// (beforeLoad 在 loadURL 之前执行,确保 IPC handler 注册时 mainWindow 已注入)
|
||||
|
||||
// ===== 系统托盘 =====
|
||||
const resourcesPath = join(__dirname, '../../assets');
|
||||
@@ -432,15 +470,6 @@ async function initialize(): Promise<void> {
|
||||
);
|
||||
});
|
||||
|
||||
// ===== 注册 IPC =====
|
||||
registerAllIPCHandlers(
|
||||
mainWindow, sessionService, configService, workspaceService,
|
||||
contextBuilder, agentLoop, toolRegistry, auditService,
|
||||
sessionRecorder, memoryManager, mcpManager, reloadAdapter,
|
||||
promptDefender, outputValidator,
|
||||
confirmationHook, memoryConsolidator, orchestrator,
|
||||
);
|
||||
|
||||
// TODO: Initialize UpdateService for auto-update functionality
|
||||
// const updateService = new UpdateService();
|
||||
// updateService.initialize(mainWindow);
|
||||
|
||||
Reference in New Issue
Block a user