v0.16.7: 引擎修复 + 工具面板统一 + AGENT.md 改为仅工作空间加载
核心引擎修复: - 状态转换表补全 THINKING/PARSING/EXECUTING -> COMPRESSING,修复紧急压缩成为死代码的 P1 问题 - 新增跨轮次死循环检测器(软性提示 + 硬性熔断),防止模型陷入重复工具调用死循环 - handleCompressing 空响应回到 THINKING 而非 REFLECTING,避免错误终止 - executeHooks 添加 .catch() 防止未处理的 Promise 拒绝 - ALWAYS_PARALLEL 移除 git 和 browser_evaluate(有副作用的工具不应并行) - thinking fallback:content 为空但有 thinking 时,用 [推理过程] 作为 content 保留上下文 - 8个写类工具添加专用格式化器(含 success + message 字段) - 清理死代码:3个未使用函数 + 3个未使用 import 工具面板统一: - 10个工具独立下拉框统一为1个全局执行模式选择器 - FIFO 队列防止并行 showToolConfirm 导致静默取消 - delete_file 支持 paths 数组参数批量删除 工具定义与实现一致性修复: - run_command 移除未使用的 timeout 参数,描述改为"超时可配置" - list_directory 添加 2000 条截断逻辑 + filter_extension 参数 - calculator 正则移除 ^ 字符(parser 用 ** 替代) - search_files/tree/web_search/fetch_top 描述与实现对齐 消息传递修复: - trimByTokenLimit 改为原子组选择(assistant+tool_calls 与后续 tool 消息作为一组) - 历史工具结果复用 formatToolResultForModel,与当前格式一致 AGENT.md 加载策略变更: - 删除内置 AGENT.md 文件 - 仅从工作空间加载:有则注入,无则跳过 其他修复: - 修复初始化失败 "Cannot convert undefined or null to object"(saveSetting null 导致 JSON.parse 陷阱) - 修复工作空间命令行标签页 idle 状态残留导致样式错乱 版本号: 0.16.5 -> 0.16.7
This commit is contained in:
+70
-78
@@ -22,7 +22,7 @@ import { initHistoryModal, closeHistoryModal } from './components/history-modal.
|
||||
import { initMemoryModal } from './components/memory-modal.js';
|
||||
import { initToolsModal, closeToolsModal } from './components/tools-modal.js';
|
||||
import { initTokenDashboard, closeTokenDashboard } from './components/token-dashboard.js';
|
||||
import { setToolEnabled, setToolMode, refreshMCPTools } from './services/tool-registry.js';
|
||||
import { initGlobalToolMode, refreshMCPTools } from './services/tool-registry.js';
|
||||
import { startAllMCPServers } from './services/mcp-client.js';
|
||||
import { initToolConfirmModal } from './components/tool-confirm-modal.js';
|
||||
import { initWorkspacePanel, clearToolCardsExternal, clearTerminalExternal, switchToTab } from './components/workspace-panel.js';
|
||||
@@ -296,6 +296,52 @@ async function init(): Promise<void> {
|
||||
|
||||
logInit('启动...');
|
||||
|
||||
// ── UI 组件初始化(不依赖数据库,先初始化以避免 catch 路径重复绑定)──
|
||||
initLogPanel(); // ← 日志面板最先初始化,确保后续 init 日志能显示
|
||||
initGlobalErrorHandler(); // R42: 全局错误处理器(紧随日志面板之后)
|
||||
initToast();
|
||||
initLightbox();
|
||||
initHeader();
|
||||
initModelBar();
|
||||
initChatArea();
|
||||
initInputArea();
|
||||
initSettingsModal();
|
||||
initHistoryModal();
|
||||
initHelpModal();
|
||||
initToolConfirmModal();
|
||||
initMemoryModal();
|
||||
initToolsModal();
|
||||
initTokenDashboard();
|
||||
initWorkspacePanel();
|
||||
setupDesktopIntegration();
|
||||
initSearxngModal();
|
||||
|
||||
bindGlobalEvents();
|
||||
|
||||
// ── Harness Engineering 系统初始化(幂等,UI 就绪后即可注册)──
|
||||
initHarnessHooks();
|
||||
// Agent Metrics 无需显式初始化(按需启动)
|
||||
// Context Indexer 在 Agent INIT 状态按需构建
|
||||
|
||||
// ── 状态默认值(不依赖数据库)──
|
||||
state.set(KEYS.CURRENT_SESSION, createNewSession());
|
||||
state.set(KEYS.IS_STREAMING, false);
|
||||
state.set(KEYS.IS_HISTORY_VIEW, false);
|
||||
state.set(KEYS.NUM_CTX, 131072); // 默认 128K
|
||||
|
||||
// ── 防御性重置:确保首次启动不会遗留脏状态 ──
|
||||
state.set('toolCallingEnabled', true);
|
||||
state.set('runCommandMode', 'confirm'); // 兼容旧代码读取
|
||||
state.set('thinkEnabled', false);
|
||||
|
||||
// ── 注入应用版本号到 Agent Metrics ──
|
||||
const _bridge = window.metonaDesktop;
|
||||
if (_bridge?.info) {
|
||||
_bridge.info().then((info: { version?: string }) => {
|
||||
if (info?.version) setAppVersion(info.version);
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
try {
|
||||
db = new ChatDB();
|
||||
await db.init();
|
||||
@@ -309,37 +355,6 @@ async function init(): Promise<void> {
|
||||
state.set(KEYS.API, api);
|
||||
logInit(`Ollama 地址: ${serverUrl}`);
|
||||
|
||||
state.set(KEYS.CURRENT_SESSION, createNewSession());
|
||||
state.set(KEYS.IS_STREAMING, false);
|
||||
state.set(KEYS.IS_HISTORY_VIEW, false);
|
||||
state.set(KEYS.NUM_CTX, 131072); // 默认 128K
|
||||
|
||||
// ── 防御性重置:确保首次启动不会遗留脏状态 ──
|
||||
state.set('toolCallingEnabled', true);
|
||||
state.set('runCommandMode', 'confirm'); // 兼容旧代码读取
|
||||
state.set('thinkEnabled', false);
|
||||
|
||||
initLogPanel(); // ← 日志面板最先初始化,确保后续 init 日志能显示
|
||||
initGlobalErrorHandler(); // R42: 全局错误处理器(紧随日志面板之后)
|
||||
initToast();
|
||||
initLightbox();
|
||||
initHeader();
|
||||
initModelBar();
|
||||
initChatArea();
|
||||
initInputArea();
|
||||
initSettingsModal();
|
||||
initHistoryModal();
|
||||
initHelpModal();
|
||||
initToolConfirmModal();
|
||||
initMemoryModal();
|
||||
initToolsModal();
|
||||
initTokenDashboard();
|
||||
initWorkspacePanel();
|
||||
setupDesktopIntegration();
|
||||
initSearxngModal();
|
||||
|
||||
bindGlobalEvents();
|
||||
|
||||
await checkConnection();
|
||||
await loadModels();
|
||||
await loadSearxngConfig(db);
|
||||
@@ -349,19 +364,6 @@ async function init(): Promise<void> {
|
||||
initMemoryFile().catch(() => {});
|
||||
});
|
||||
|
||||
// ── Harness Engineering 系统初始化 ──
|
||||
initHarnessHooks();
|
||||
// Agent Metrics 无需显式初始化(按需启动)
|
||||
// Context Indexer 在 Agent INIT 状态按需构建
|
||||
|
||||
// ── 注入应用版本号到 Agent Metrics ──
|
||||
const _bridge = window.metonaDesktop;
|
||||
if (_bridge?.info) {
|
||||
_bridge.info().then((info: { version?: string }) => {
|
||||
if (info?.version) setAppVersion(info.version);
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
logInit('所有组件已就绪(含 Harness)');
|
||||
|
||||
const savedModel = await db.getSetting('selectedModel', '');
|
||||
@@ -444,22 +446,29 @@ async function init(): Promise<void> {
|
||||
|
||||
// ── Tool Calling 设置(永久开启,不可关闭)──
|
||||
state.set('toolCallingEnabled', true);
|
||||
// 统一加载所有工具的执行模式(auto/confirm/disabled)
|
||||
// 全局工具执行模式(统一管理所有工具,不区分工具)
|
||||
{
|
||||
// 优先读取合并存储的 toolModes JSON
|
||||
const savedModes = await db.getSetting<Record<string, 'auto' | 'confirm' | 'disabled'>>('toolModes', {});
|
||||
// 兼容旧版:如果 toolModes 中没有 run_command 但有独立的 runCommandMode,则迁移
|
||||
if (!savedModes['run_command']) {
|
||||
const legacy = await db.getSetting('runCommandMode', 'confirm') as 'auto' | 'confirm' | 'disabled';
|
||||
savedModes['run_command'] = legacy;
|
||||
await db.saveSetting('toolModes', savedModes);
|
||||
await db.saveSetting('runCommandMode', null); // 清除旧键
|
||||
}
|
||||
state.set('toolModes', savedModes);
|
||||
// 为每个工具应用模式
|
||||
for (const [toolName, mode] of Object.entries(savedModes)) {
|
||||
setToolMode(toolName, mode);
|
||||
// 读取全局模式,默认 confirm
|
||||
let globalMode = await db.getSetting<'auto' | 'confirm'>('globalToolMode', 'confirm');
|
||||
// 兼容旧版:从旧的 toolModes 迁移(如果存在)
|
||||
// 注意:之前用 saveSetting('toolModes', null) 清除旧键,JSON.parse("null") 返回 null
|
||||
// 所以这里必须做 null 防御,否则 Object.keys(null) 会抛出 "Cannot convert undefined or null to object"
|
||||
const savedModes = await db.getSetting<Record<string, 'auto' | 'confirm' | 'disabled'>>('toolModes', {}) ?? {};
|
||||
if (savedModes && Object.keys(savedModes).length > 0) {
|
||||
// 如果旧 toolModes 中所有被管理工具都是 auto,则迁移为 auto;否则 confirm
|
||||
const allAuto = [
|
||||
'run_command', 'write_file', 'create_directory', 'delete_file',
|
||||
'edit_file', 'replace_in_files', 'move_file', 'copy_file',
|
||||
'download_file', 'compress',
|
||||
].every(t => savedModes[t] === 'auto');
|
||||
globalMode = allAuto ? 'auto' : 'confirm';
|
||||
await db.saveSetting('globalToolMode', globalMode);
|
||||
// 清除旧键:用空对象而非 null,避免 JSON.parse("null") 返回 null 的陷阱
|
||||
await db.saveSetting('toolModes', {});
|
||||
}
|
||||
state.set('globalToolMode', globalMode);
|
||||
// 应用全局模式到所有被管理工具
|
||||
initGlobalToolMode(globalMode);
|
||||
}
|
||||
|
||||
// ── Plan 自动确认设置 ──
|
||||
@@ -478,7 +487,8 @@ async function init(): Promise<void> {
|
||||
logSuccess('应用初始化完成');
|
||||
} catch (err) {
|
||||
logError('初始化失败', (err as Error).message);
|
||||
if (!api!) {
|
||||
// UI 组件已在 try 块之前初始化,此处不重复调用以避免重复绑定事件监听器
|
||||
if (!api) {
|
||||
api = new OllamaAPI();
|
||||
state.set(KEYS.API, api);
|
||||
}
|
||||
@@ -486,24 +496,6 @@ async function init(): Promise<void> {
|
||||
state.set(KEYS.IS_STREAMING, false);
|
||||
state.set(KEYS.IS_HISTORY_VIEW, false);
|
||||
state.set(KEYS.CURRENT_SESSION, createNewSession());
|
||||
initLogPanel();
|
||||
bindGlobalEvents();
|
||||
initToast();
|
||||
initLightbox();
|
||||
initHeader();
|
||||
initModelBar();
|
||||
initChatArea();
|
||||
initInputArea();
|
||||
initSettingsModal();
|
||||
initHistoryModal();
|
||||
initHelpModal();
|
||||
initToolConfirmModal();
|
||||
initMemoryModal();
|
||||
initToolsModal();
|
||||
initTokenDashboard();
|
||||
initWorkspacePanel();
|
||||
setupDesktopIntegration();
|
||||
initHarnessHooks();
|
||||
// ── MEMORY.md 初始化(fallback 路径)──
|
||||
import('./services/memory-service.js').then(({ initMemoryFile }) => {
|
||||
initMemoryFile().catch(() => {});
|
||||
|
||||
Reference in New Issue
Block a user