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:
@@ -59,12 +59,31 @@ export function registerAllIPCHandlers(
|
||||
|
||||
ipcMain.handle('agent:sendMessage', async (_event, userMessage: MetonaMessage, sessionId: string) => {
|
||||
// M-33 修复: 参数校验,防止 undefined/非字符串导致下游异常
|
||||
// P1-5 修复: 校验失败时也发 ERROR+DONE 流事件,防止 isStreaming 永久卡死
|
||||
if (!sessionId || typeof sessionId !== 'string') {
|
||||
log.warn('[AGENT] sendMessage rejected: invalid sessionId');
|
||||
if (!mainWindow.isDestroyed()) {
|
||||
const errorEvent: MetonaStreamEvent = {
|
||||
type: MetonaStreamEventType.ERROR,
|
||||
requestId: '', sessionId: sessionId ?? '', iteration: 0, seq: 0, timestamp: Date.now(),
|
||||
error: { code: MetonaErrorCode.UNKNOWN, message: '无效的会话 ID', retryable: false },
|
||||
};
|
||||
mainWindow.webContents.send('agent:streamEvent', errorEvent);
|
||||
mainWindow.webContents.send('agent:streamEvent', { ...errorEvent, type: MetonaStreamEventType.DONE });
|
||||
}
|
||||
return { success: false, error: 'Invalid sessionId' };
|
||||
}
|
||||
if (!userMessage || typeof userMessage !== 'object' || typeof userMessage.content !== 'string') {
|
||||
log.warn('[AGENT] sendMessage rejected: invalid userMessage');
|
||||
if (!mainWindow.isDestroyed()) {
|
||||
const errorEvent: MetonaStreamEvent = {
|
||||
type: MetonaStreamEventType.ERROR,
|
||||
requestId: '', sessionId, iteration: 0, seq: 0, timestamp: Date.now(),
|
||||
error: { code: MetonaErrorCode.UNKNOWN, message: '无效的消息格式', retryable: false },
|
||||
};
|
||||
mainWindow.webContents.send('agent:streamEvent', errorEvent);
|
||||
mainWindow.webContents.send('agent:streamEvent', { ...errorEvent, type: MetonaStreamEventType.DONE });
|
||||
}
|
||||
return { success: false, error: 'Invalid message format' };
|
||||
}
|
||||
log.info('[AGENT] sendMessage:', sessionId, (userMessage.content ?? '').slice(0, 80));
|
||||
@@ -404,6 +423,9 @@ export function registerAllIPCHandlers(
|
||||
ipcMain.handle('agent:abortSession', async (_event, sessionId) => {
|
||||
log.info('[AGENT] Abort:', sessionId);
|
||||
agentLoop.abort();
|
||||
// MT-1 修复: 等待当前 run 完全结束再返回,防止用户立即重发时新消息卡在
|
||||
// runStream 的 currentRunPromise 等待中(最长卡 120s 工具超时)
|
||||
await agentLoop.waitForAbort();
|
||||
// v0.3.0 修复: 清理所有等待中的工具确认,避免定时器泄漏和超时 toast 在新会话中弹出
|
||||
confirmationHook.clearPending();
|
||||
|
||||
@@ -670,10 +692,11 @@ export function registerAllIPCHandlers(
|
||||
// v0.3.1: 加入 deepseek.contextWindow / agnes.contextWindow / mimo.contextWindow,使上下文窗口配置变化也触发热重载
|
||||
if (['llm.provider', 'llm.model', 'llm.apiKey', 'llm.baseURL', 'ollama.numCtx',
|
||||
'deepseek.contextWindow', 'agnes.contextWindow', 'mimo.contextWindow'].includes(key)) {
|
||||
// C-1 修复: reloadAdapter 返回 false 时表示加载失败,需要通知前端
|
||||
// P0-3 修复: reloadAdapter 失败时返回 success:false,让前端知道配置不完整
|
||||
const reloadSuccess = reloadAdapter();
|
||||
if (!reloadSuccess) {
|
||||
log.warn(`[CONFIG] Adapter reload failed after ${key} change`);
|
||||
return { success: false, error: 'LLM 配置不完整,请检查 Provider、API Key、Base URL 和 Model 是否都已填写' };
|
||||
} else {
|
||||
log.info(`[CONFIG] LLM config changed (${key}), adapter reloaded`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user