feat: v0.7.0 四阶段全量迭代 — 修复面收口 · 安全纵深 · 架构还债 · 能力演进
P1 修复面收口: v0.6.3 截断自愈推全量(Anthropic/Ollama/非流式/引擎兜底); SSE 上游错误帧检测进重试通道; clearMessages 摘要游标根治; truncateResult 内联图片白名单统一; 前端四 bug(确认弹窗锁死/MemoryViewer/ Virtuoso Footer/abort 尾部过滤) + reasoning 缓冲跨迭代污染; 托盘通知过滤与新建会话死链接线 P2 安全纵深: MCP 审批闭环(ConfirmationHook×PolicyEngine 联动+重名拒注册); SSRF 收敛 ssrf-guard 共享模块 (web_fetch 双通道校验+重定向终态复检); Electron 加固(preload CJS 化→sandbox:true/CSP/权限白名单/will-navigate); run_command cmd.exe 白名单通道元字符守门; diff_viewer 10MB 预检; Anthropic thinking 预算下限; Agnes 思考显式关闭 P3 架构还债: OpenAICompatibleAdapter 中间基类收敛四家样板; 错误分类单轨化(删 mapError/getFetchSignal, 超时显式 ETIMEDOUT); PRAGMA user_version 迁移版本化; 死代码清理专项(cn.ts/SHORTCUTS/ContextMenu 分支/ getWindowState/modifiedArgs/sandbox 空壳); i18next 引入; a11y 第一轮; SearXNG 页批量草稿模型统一 P4 能力演进: Ollama pull 可取消/capabilities 探测/num_ctx 实测缓存; UpdateService feed 比对式自动更新 (app:updateCheck IPC + StatusBar 入口); MiMo providerOptions(web_search 服务端工具/strict JSON); web_fetch extract_mode=markdown(turndown); network.proxyUrl 全局代理(Chromium sessions+undici dispatcher) 测试: 264 → 507 用例(Electron ABI 全绿零跳过), 覆盖引擎压缩管线/重试竞速/MEMORY.md 闸门/file_editor 五操作/ filesystem 七工具实体夹具/git 真实仓库/SSE 错误帧/全线截断自愈/Provider 请求形态矩阵/SSRF 表测/钩子分级矩阵/ OutputValidator 全量/SLO 指标/MCP 安全纯函数/task_manager 链路/渲染层纯域/i18n 桥契约
This commit is contained in:
@@ -248,3 +248,78 @@ export function buildSearXNGAuthHeaders(authKey: string, authType: string): Reco
|
||||
export function logTool(toolName: string, message: string): void {
|
||||
log.info(`[Tool:${toolName}] ${message}`);
|
||||
}
|
||||
|
||||
// ===== v0.6.4 P4-4: HTML → Markdown 转换(web_fetch extract_mode='markdown') =====
|
||||
//
|
||||
// v0.6.4 收尾:私有 npm 凭据解锁后,按开发规范第一铁律把第一轮的临时自写实现
|
||||
// 替换为 turndown(成熟库)。对外函数签名与行为契约保持不变:
|
||||
// h1-h6(atx) / 段落 / 链接 / 图片 / strong+em+code 行内 / pre 围栏代码块 /
|
||||
// ul('-') 与 ol(数字) 列表(跨空行合并为紧凑形态) / blockquote / hr('---') /
|
||||
// 表格等未知块降级为纯文本、<script/style/svg/noscript/iframe> 整体剔除。
|
||||
|
||||
import TurndownService from 'turndown';
|
||||
|
||||
const turndown = new TurndownService({
|
||||
headingStyle: 'atx',
|
||||
bulletListMarker: '-',
|
||||
codeBlockStyle: 'fenced',
|
||||
emDelimiter: '*',
|
||||
});
|
||||
|
||||
// 噪声节点显式剔除(与 htmlToText 的剥离口径一致)
|
||||
turndown.remove(['script', 'style', 'noscript', 'iframe', 'svg']);
|
||||
|
||||
// hr 输出 GitHub 风格 '---'(turndown 默认 '* * *')
|
||||
turndown.addRule('hr-rule', {
|
||||
filter: ['hr'],
|
||||
replacement: () => '\n\n---\n\n',
|
||||
});
|
||||
|
||||
/** 列表项行判定:'- xxx' 或 '1. xxx'(允许前导空白) */
|
||||
const LIST_LINE = /^\s*(?:- |\d+\. )/;
|
||||
|
||||
/**
|
||||
* 紧凑化 + 规范化列表 —— turndown 对松散列表(li 之间带空白文本节点的常见书写)
|
||||
* 输出条目间空行,且标记为 '- ' / '1. ' 多空格形态。这里做单趟扫描:
|
||||
* 1. 归一化条目标记为紧凑形态('- ' / 'N. ');
|
||||
* 2. 仅当"空行两侧都是同一列表的条目行"时移除该空行(绝不吞条目、不影响段落间距)。
|
||||
*/
|
||||
function collapseListGaps(markdown: string): string {
|
||||
const lines = markdown.split('\n').map((line) =>
|
||||
line
|
||||
.replace(/^(\s*)- {2,}/, '$1- ')
|
||||
.replace(/^(\s*\d+\.)\s{2,}/, '$1 '),
|
||||
);
|
||||
|
||||
const isListItem = (l: string | undefined): boolean => (l ?? '').length > 0 && LIST_LINE.test(l!);
|
||||
|
||||
const out: string[] = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (line.trim() === '') {
|
||||
const prev = out.length > 0 ? out[out.length - 1] : undefined;
|
||||
const next = i + 1 < lines.length ? lines[i + 1] : undefined;
|
||||
// 空行夹在两个列表项之间 → 移除;否则保留原始段落间隔
|
||||
if (isListItem(prev) && isListItem(next)) continue;
|
||||
out.push(line);
|
||||
continue;
|
||||
}
|
||||
out.push(line);
|
||||
}
|
||||
return out.join('\n');
|
||||
}
|
||||
|
||||
export function htmlToMarkdown(html: string): string {
|
||||
if (!html || !html.trim()) return '';
|
||||
|
||||
let md: string;
|
||||
try {
|
||||
md = turndown.turndown(html);
|
||||
} catch {
|
||||
// 极端畸形输入时降级为空串(调用方已具备 Phase1 文本回退能力)
|
||||
logTool?.('htmlToMarkdown', 'turndown conversion failed');
|
||||
return '';
|
||||
}
|
||||
|
||||
return collapseListGaps(md).replace(/\n{3,}/g, '\n\n').trim();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user