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:
@@ -881,14 +881,44 @@ function trimByTokenLimit(messages: OllamaMessage[], maxTokens: number): OllamaM
|
||||
const recentMsgs = nonSystemMsgs.slice(-PROTECT_RECENT);
|
||||
const olderMsgs = nonSystemMsgs.slice(0, -PROTECT_RECENT);
|
||||
|
||||
// 计算每条消息的 token + 重要性
|
||||
const scored = olderMsgs.map(m => ({
|
||||
msg: m,
|
||||
tokens: estimateTokens(m.content || '') +
|
||||
(m.images ? m.images.length * 100 : 0) +
|
||||
(m.tool_calls ? m.tool_calls.length * 50 : 0),
|
||||
importance: scoreMessageImportance(m),
|
||||
}));
|
||||
// 原子组分组:assistant(带 tool_calls) + 其后续的 tool 消息作为一组
|
||||
// 避免裁剪时破坏 assistant.tool_calls 与 tool 结果的配对关系
|
||||
interface MsgGroup {
|
||||
msgs: OllamaMessage[];
|
||||
tokens: number;
|
||||
importance: number;
|
||||
originalIndex: number;
|
||||
}
|
||||
|
||||
const groups: MsgGroup[] = [];
|
||||
let i = 0;
|
||||
while (i < olderMsgs.length) {
|
||||
const msg = olderMsgs[i];
|
||||
if (msg.role === 'assistant' && msg.tool_calls?.length) {
|
||||
// 原子组:assistant(带 tool_calls) + 后续连续的 tool 消息
|
||||
const groupMsgs: OllamaMessage[] = [msg];
|
||||
let tokens = estimateTokens(msg.content || '') +
|
||||
(msg.images ? msg.images.length * 100 : 0) +
|
||||
(msg.tool_calls ? msg.tool_calls.length * 50 : 0);
|
||||
let maxImportance = scoreMessageImportance(msg);
|
||||
let j = i + 1;
|
||||
while (j < olderMsgs.length && olderMsgs[j].role === 'tool') {
|
||||
const toolMsg = olderMsgs[j];
|
||||
groupMsgs.push(toolMsg);
|
||||
tokens += estimateTokens(toolMsg.content || '');
|
||||
maxImportance = Math.max(maxImportance, scoreMessageImportance(toolMsg));
|
||||
j++;
|
||||
}
|
||||
groups.push({ msgs: groupMsgs, tokens, importance: maxImportance, originalIndex: i });
|
||||
i = j;
|
||||
} else {
|
||||
const tokens = estimateTokens(msg.content || '') +
|
||||
(msg.images ? msg.images.length * 100 : 0) +
|
||||
(msg.tool_calls ? msg.tool_calls.length * 50 : 0);
|
||||
groups.push({ msgs: [msg], tokens, importance: scoreMessageImportance(msg), originalIndex: i });
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// system 消息的 token 消耗
|
||||
const systemTokens = systemMsgs.reduce((sum, m) => sum + estimateTokens(m.content || ''), 0);
|
||||
@@ -904,27 +934,29 @@ function trimByTokenLimit(messages: OllamaMessage[], maxTokens: number): OllamaM
|
||||
}
|
||||
|
||||
// R94: 按综合评分降序排列(重要性 + 时近性),取能装下的最大数量
|
||||
const totalOlder = olderMsgs.length;
|
||||
scored.forEach((s, idx) => {
|
||||
const totalGroups = groups.length;
|
||||
groups.forEach((g, idx) => {
|
||||
// R94: 时近性因子 — 越靠近最近窗口的消息得分越高(0~2 分加成)
|
||||
const recencyRatio = totalOlder > 1 ? idx / (totalOlder - 1) : 1;
|
||||
s.importance += Math.round(recencyRatio * 2);
|
||||
const recencyRatio = totalGroups > 1 ? idx / (totalGroups - 1) : 1;
|
||||
g.importance += Math.round(recencyRatio * 2);
|
||||
});
|
||||
scored.sort((a, b) => b.importance - a.importance);
|
||||
// 按重要性排序(降序),但保留原始索引用于重建
|
||||
const sortedGroups = [...groups].sort((a, b) => b.importance - a.importance);
|
||||
|
||||
let usedTokens = 0;
|
||||
const kept = new Set<number>(); // 保留的索引
|
||||
for (let i = 0; i < scored.length; i++) {
|
||||
if (usedTokens + scored[i].tokens > availableTokens && kept.size >= 2) break;
|
||||
usedTokens += scored[i].tokens;
|
||||
kept.add(i);
|
||||
const keptOriginalIndices = new Set<number>();
|
||||
for (let i = 0; i < sortedGroups.length; i++) {
|
||||
if (usedTokens + sortedGroups[i].tokens > availableTokens && keptOriginalIndices.size >= 2) break;
|
||||
usedTokens += sortedGroups[i].tokens;
|
||||
keptOriginalIndices.add(sortedGroups[i].originalIndex);
|
||||
}
|
||||
|
||||
// 按原始顺序重建:system → 按重要性保留的旧消息 → 最近的 protected 消息
|
||||
// 按原始顺序重建:system → 按重要性保留的旧消息组 → 最近的 protected 消息
|
||||
const result: OllamaMessage[] = [...systemMsgs];
|
||||
const keptSet = new Set(scored.filter((_, i) => kept.has(i)).map(s => s.msg));
|
||||
for (const m of olderMsgs) {
|
||||
if (keptSet.has(m)) result.push(m);
|
||||
for (const g of groups) {
|
||||
if (keptOriginalIndices.has(g.originalIndex)) {
|
||||
result.push(...g.msgs);
|
||||
}
|
||||
}
|
||||
result.push(...recentMsgs);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user