feat: v0.3.18 上下文压缩机制修复 + MCP 工具就绪竞态修复 + 记忆系统加固
【上下文压缩机制修复】 - engine.ts 新增 lastRealInputTokens 记录 LLM 返回的真实输入 token,压缩判断取 max(估算值, 真实值),避免估算偏低导致不压缩但 API 413 - 修复 effectiveContextWindow 缺少默认值导致 compressionThreshold 变 NaN、压缩永不触发的 bug(添加 ?? 128_000 兜底) - compressMessages 保留区从固定 10 条改为按 token 预算动态截断(50% 上下文窗口) - 二次截断 charsPerToken 从 2 调整为 1.0,与 CJK_TOKEN_RATIO 一致 - 压缩后重置 lastRealInputTokens,避免跨迭代污染 - 每个 run 开始时重置 lastRealInputTokens 【前端 Token 显示修复】 - 区分"累计消耗"和"上下文占用"语义——之前 totalTokens(累计) / contextWindow(单次窗口) 得出无意义百分比 - TokenUsage.tsx 上下文占用改用 lastInputTokens,新增压缩节省行(绿色,仅当 > 0 时显示) - agent-store.ts TokenUsage 接口新增 lastInputTokens 和 lastCompressedSaved 字段,4 处初始值统一更新 - useAgentStream.ts usage 事件 lastInputTokens 替换不累加,compressed 事件通过 streamEvent 接收 savedTokens - handlers.ts onCompressed 同时发 toast + streamEvent,解决"压缩触发但前端 token 显示不降"缺陷 - 旧数据兼容使用 ?? 0,保证历史会话加载不崩溃 【MCP 工具就绪竞态修复】 - 修复输入框永久显示"工具加载中"的竞态条件:MCP initialize 几乎立即 resolve(connectServer 不 await),tools:ready 事件在前端监听器注册前已发出 - main.ts 维护 toolsReady 标志 + 注册 tools:isReady IPC handler 查询当前状态 - preload.ts 暴露 tools.isReady() 方法 - App.tsx 注册 onReady 监听器后立即查询 isReady(),无论事件是否错过都能恢复正确状态 【记忆系统加固】 - consolidator.ts 新增 runningPromise + waitForCompletion(35s),before-quit 等待固化完成,防止退出时异步 consolidate 数据丢失 - 固化到 MEMORY.md 的同时写入 semantic_memories 表,解决双轨存储无交叉验证问题 - manager.ts tokenize 按中英文标点切分子句后再做 bigram,优化中文分词 - 清理正则冗余括号 【SOUL.md 降级处理】 - context-builder.ts SOUL.md 为空或不存在时降级到默认身份,向前端发 toast 提示用户 - 新增 fallbackRoleNotified 去重标志,仅首次降级通知,避免每次发消息都弹 toast - SOUL.md 恢复内容时重置标志 【Token 估算调整】 - token-estimator.ts CJK_TOKEN_RATIO 从 1.5 调整为 1.0 【MCP 异步初始化】 - main.ts MCP 完成后广播 tools:ready 事件,前端 UI 据以控制输入框可用性 - preload.ts + global.d.ts 暴露 tools.onReady() 监听器 - App.tsx + ChatInput.tsx toolsReady 状态控制输入框 【版本号】 - package.json + package-lock.json 从 0.3.16 升级到 0.3.18
This commit is contained in:
+39
-3
@@ -14,7 +14,7 @@
|
||||
* @see docs/MetonaAI-Desktop UI UX 设计集成方案.html — 窗口管理
|
||||
*/
|
||||
|
||||
import { app, shell, Menu, BrowserWindow } from 'electron';
|
||||
import { app, shell, Menu, BrowserWindow, ipcMain } from 'electron';
|
||||
import { join } from 'path';
|
||||
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { electronApp, optimizer } from '@electron-toolkit/utils';
|
||||
@@ -331,15 +331,34 @@ async function initialize(): Promise<void> {
|
||||
agentLoop.setWorkspacePath(workspaceInfo.path);
|
||||
|
||||
// P1-7 修复: MCP 初始化在 agentLoop 创建后执行,完成后重新同步工具到 AgentLoop
|
||||
// v0.3.18 修复: MCP 完成后广播 tools:ready 事件,前端据以启用发送按钮
|
||||
// 之前 MCP 初始化是异步的,用户在 MCP 完成前发送消息会用到不全的工具集
|
||||
// v0.3.18 修复: 维护 toolsReady 标志 + 暴露 tools:isReady 查询,解决竞态条件
|
||||
// MCP initialize 几乎立即 resolve(connectServer 不 await),tools:ready 事件
|
||||
// 可能在前端监听器注册前就发出,导致前端永久错过事件、输入框永久禁用
|
||||
let toolsReady = false;
|
||||
mcpManager.initialize().then(() => {
|
||||
agentLoop.setTools(toolRegistry.listTools());
|
||||
log.info('[MCP] Tools registered and synced to AgentLoop');
|
||||
toolsReady = true;
|
||||
// 广播工具就绪事件给所有窗口
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
win.webContents.send('tools:ready', { toolCount: toolRegistry.size });
|
||||
}
|
||||
}).catch((err) => {
|
||||
log.warn('MCP Manager initialization error:', err);
|
||||
// 即使 MCP 失败,内置工具已就绪,仍广播 ready 让前端启用发送
|
||||
toolsReady = true;
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
win.webContents.send('tools:ready', { toolCount: toolRegistry.size });
|
||||
}
|
||||
});
|
||||
|
||||
// ===== Memory Consolidator(会话结束 AI 提取重要记忆到 MEMORY.md)=====
|
||||
const memoryConsolidator = new MemoryConsolidator(adapter, workspaceService);
|
||||
// v0.3.18 修复: 注入 MemoryManager,使 consolidate 同步写入 semantic_memories 表
|
||||
// 实现 DB 记忆与 MEMORY.md 双轨交叉,避免"AI 记得一半"的不一致
|
||||
memoryConsolidator.setMemoryManager(memoryManager);
|
||||
|
||||
// ===== Task Orchestrator(子任务委派)=====
|
||||
const orchestrator = new TaskOrchestrator(
|
||||
@@ -473,6 +492,9 @@ async function initialize(): Promise<void> {
|
||||
promptDefender, outputValidator,
|
||||
confirmationHook, memoryConsolidator, orchestrator,
|
||||
);
|
||||
// v0.3.18 修复: 注册 tools:isReady 查询,前端注册 onReady 监听器后立即查询,
|
||||
// 解决 tools:ready 事件在监听器注册前发出的竞态条件
|
||||
ipcMain.handle('tools:isReady', () => ({ ready: toolsReady, toolCount: toolRegistry.size }));
|
||||
},
|
||||
});
|
||||
|
||||
@@ -541,12 +563,13 @@ async function initialize(): Promise<void> {
|
||||
(global as Record<string, unknown>).shutdownInProgress = true;
|
||||
|
||||
event.preventDefault();
|
||||
// 内部异步清理,加 5 秒超时保护防止卡死
|
||||
// v0.3.18 修复: 超时从 5 秒延长到 40 秒,以容纳 consolidate 的 35 秒等待
|
||||
// 之前 5 秒超时会导致进行中的 consolidate 被 kill,记忆丢失
|
||||
const shutdownTimeout = setTimeout(() => {
|
||||
log.warn('[Shutdown] Timeout reached, forcing exit');
|
||||
if (databaseService) { databaseService.close(); databaseService = null; }
|
||||
app.exit(0);
|
||||
}, 5_000);
|
||||
}, 40_000);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
@@ -555,6 +578,19 @@ async function initialize(): Promise<void> {
|
||||
log.error('[Shutdown] MCP shutdown failed:', err);
|
||||
}
|
||||
cleanupBrowser();
|
||||
|
||||
// v0.3.18 修复: 等待进行中的记忆固化任务完成,避免应用退出导致记忆丢失
|
||||
// consolidate 内部有 30 秒 LLM 超时保护,此处等待 35 秒足够覆盖
|
||||
if (memoryConsolidator.isRunning()) {
|
||||
log.info('[Shutdown] Waiting for memory consolidation to complete...');
|
||||
const completed = await memoryConsolidator.waitForCompletion(35_000);
|
||||
if (!completed) {
|
||||
log.warn('[Shutdown] Memory consolidation timed out, proceeding with exit');
|
||||
} else {
|
||||
log.info('[Shutdown] Memory consolidation completed');
|
||||
}
|
||||
}
|
||||
|
||||
if (databaseService) { databaseService.close(); databaseService = null; }
|
||||
clearTimeout(shutdownTimeout);
|
||||
app.exit(0);
|
||||
|
||||
Reference in New Issue
Block a user