feat: v0.8.0 流语义补全 · 会话可靠 · 恢复力 — finish_reason 全链路贯通根治"思考中停止" · 2445 用例全量回归
CI / 类型检查 + Lint + 单元测试 (push) Failing after 9m16s
CI / 全量测试 (Electron ABI) (push) Failing after 6m4s
CI / 产物编译验证 (push) Successful in 11m1s

P0 会话可靠性收口(根治"模型思考着会话就停止"):
- P0-1 finish_reason 全链路贯通:DONE 事件与 IterationStep 新增 finishReason,OpenAI 共享 SSE / Anthropic message_delta.stop_reason / Ollama done_reason 三路采集,TRACE 层弃用硬编码 'stop' 记录真值
- P0-2 空响应守卫 + 降级重试:零产出流→可重试错误走退避;思考耗尽输出预算(reasoning-only + length)→自动关闭思考降级重试一次;仍失败→OUTPUT_LENGTH_EXCEEDED 结构化错误 + 故障转移;附带根治 abort 恰逢零工具调用轮被 COMPLETED 抢占的真实缺陷
- P0-3 思考×能力×预算三对齐:DeepSeek/MiMo/Agnes/Ollama 四家 supportsThinking=false 强制不发思考参数;小输出预算告警;设置页联动提示
- P0-4 渲染层可见性:截断/空完成/友好错误三类提示,i18n 全部出层
- P0-5 回归四件套:reasoning-only 终止判定、集成级空闲超时、504 引擎重试归类、思考中 abort→USER_INTERRUPT、P4-2 强制收尾路径

FEAT-1:LLM 设置新增「最大输出上限」——Provider 支持矩阵显隐 + 模型上限钳制提示 + 超限保存警告 + llm.maxTokens 热生效

P1 修复面收口:
- 渲染层三缺陷根治:后台会话回放缓冲(2000 条/4MB 有界 + agent:getReplayState + 事件总线)+ abort 双层自愈 + sendMessage 收尾兜底 + 中断卡片清扫
- 工具 abort 信号全覆盖:web_search/web_fetch/http_request/code_search/git 系列/delegate_task 全部接入引擎中断;web_search 时间预算收敛(720s→≤240s);移除伪造 ToolExecutionContext 与死代码
- 安全:本地 Pinned CONNECT 代理根治浏览器通道 DNS rebinding(校验期 IP pinning,可注入 resolver 表测);配置 URL 域名解析深校验(DeepCheckSoftFailure 软失败);SSE 空 error 帧防御修复;Ollama generate/embed AbortSignal.any 合并
- 缺陷清单:UTF-16 BOM 读取、tmp 同毫秒碰撞(nanoid 后缀)、code_search JS 回退参数对称(case_sensitive/前后文独立)、list_directory include_node_modules、崩溃自愈退避(60s 窗 ≥3 次停 reload)、MemoryViewer/Sidebar i18n 收口

P2 能力演进:
- 会话回收站:SCHEMA_VERSION 3 + 迁移 10(deleted_at,存在性守卫),软删除/恢复/彻底删除/30 天自动清理(启动+24h),searchMessages 聚合剔除,Sidebar 回收站面板
- 会话回放播放器:sessions:listRecordings/readRecording(白名单+目录边界+20MB 上限),SessionReplayPlayer 时间轴/步进/变速,Trace 面板入口
- electron-updater 自动更新:双轨(手动 feed 比对保留),生产环境启动静默检查 + update:status 广播 + app:updateInstall + LogsSettings UpdatePanel + builder publish 配置
- @ 文件提及:workspace.listFiles/readFileClip(边界/512KB/NUL 拒绝/MEMORY.md 保护),ChatInput Fuse 联想+键盘导航+附件管线注入
- MCP Resources/Prompts 发现:可选能力 try/catch 降级,mcp:listServerContents,MCPSettings 展开视图
- 文档对齐:内部 API 标准 HTML(Adapter 清单补 MiMo/已实现注记/STREAM_RESET/DONE.finishReason/ repetition_truncation 映射);README v0.8.0 亮点表

P3 测试基建:
- 新增 4 个测试文件:engine-stream-contract(6)、engine-stream-reliability(4:集成空闲超时/504 重试/思考中 abort/P4-2 强制收尾)、thinking-capability-gate(7)、pinned-proxy(9,含深校验 5)、session-trash(5,DB 域)、use-agent-stream hook 级(5)、agent.test 回放缓冲(2)
- 契约更新:orchestrator 被中断 SubAgent success=false(abort 优先级修复语义)、SSE 空 error 帧、UTF-16 正常读取、DeepSeek 未配置思考显式 disabled、迁移矩阵 v2→3
- 弱断言根治:registry WEBP 单向断言、hooks-contracts 自比恒真、memory 空 token 补强

全量验证:typecheck 0 错误 / lint 0 问题 / 系统 Node 2144 通过(301 DB 用例按 ABI 跳过)/ Electron ABI 2445/2445 全量通过 0 跳过
This commit is contained in:
2026-09-05 20:06:26 +08:00
parent 99d0c54129
commit 5b9d4d19b3
78 changed files with 5128 additions and 411 deletions
+126
View File
@@ -21,11 +21,42 @@ registerTranslations('zh-CN', {
'agent.system.stopped': '⏹ 会话已停止:{{reason}}',
'agent.error.generic': '错误: {{message}}',
'agent.error.content_filtered': '⚠️ {{message}}',
// v0.8.0 P2-4: @ 文件提及
'input.mention.failed': '@{{path}} 读取失败:{{reason}}',
// ===== v0.8.0 P0-2/P0-4: 流结束契约提示 =====
'agent.terminated.output_truncated':
'⚠️ 回答可能因输出上限被截断(finish_reason=length)。可在 设置 → LLM 配置 中提高"最大输出上限",或降低思考强度',
'agent.error.output_length_exceeded':
'⚠️ 模型的思考内容耗尽了输出 token 预算,未能产出回答(finish_reason=length)。建议:提高"最大输出上限" / 关闭思考模式或降低思考强度 / 更换输出预算更大的模型',
'agent.error.empty_response':
'⚠️ 模型未返回任何内容(含重试)。可能为上游中断或输出上限截断,请重试或调整输出上限/思考设置',
'agent.system.empty_completed': '⚠️ 模型未返回内容。请重试,或检查输出上限与思考模式设置',
'agent.system.finalize_missing':
'⚠️ 会话已结束但未收到正常收尾事件,已恢复输入(结果以数据库内容为准)',
'agent.system.abort_failed': '⚠️ 中断请求失败,Agent 可能仍在运行;已恢复实时显示',
'agent.system.replay_truncated': '(提示:后台运行内容较多,仅恢复了最近部分事件)',
'agent.system.tool_interrupted': '已中断',
// ===== 通用 =====
'common.cancel': '取消',
'common.delete': '删除',
'common.refresh': '刷新',
'common.close': '关闭',
// v0.8.0 P2-2: 会话回放
'replay.title': '会话回放',
'replay.file': '选择录制文件',
'replay.play': '播放',
'replay.pause': '暂停',
'replay.stepBack': '上一步',
'replay.stepForward': '下一步',
'replay.speed': '播放速度',
'replay.loadFailed': '加载录制失败',
'replay.noRecordings': '该会话还没有 TRACE 录制文件(logs/session_*.jsonl',
'replay.skippedLines': '{{count}} 行损坏/截断被跳过',
'replay.event.iterationStart': '▶ 第 {{iteration}} 轮开始',
'replay.event.iterationEnd': '■ 第 {{iteration}} 轮结束({{duration}}',
'replay.event.sessionEnd': '会话结束:{{reason}}{{iterations}} 轮)',
// ===== 工具确认弹框(ConfirmationDialog=====
'confirm.title': '工具执行确认',
@@ -68,6 +99,12 @@ registerTranslations('zh-CN', {
'sidebar.noMatch': '无匹配结果',
'sidebar.empty': '暂无会话',
'sidebar.archived': '已归档',
// v0.8.0 P2-1: 回收站
'sidebar.trash': '回收站(30 天后自动清理)',
'sidebar.trash.empty': '回收站为空',
'sidebar.trash.purge': '彻底删除',
'sidebar.trash.purgeTitle': '彻底删除会话?',
'sidebar.trash.purgeBody': '「{{title}}」及其全部消息将被永久删除,此操作不可恢复。',
'sidebar.count': '{{count}} 个',
'sidebar.restore': '恢复',
'sidebar.deleteTitle': '删除会话',
@@ -159,6 +196,13 @@ registerTranslations('zh-CN', {
'llm.ctxWindow.mimoHelper': '默认 10000001M),用于上下文压缩判断',
'llm.ctxWindow.oaHelper': 'gpt-4o 默认 128Kgpt-4.1 默认 1M',
'llm.ctxWindow.anthropicHelper': 'Claude 默认 200K',
// v0.8.0 FEAT-1: 最大输出上限(llm.maxTokens,按请求传给 API
'llm.maxTokens.label': '最大输出上限 (tokens)',
'llm.maxTokens.helper': '单次回复(含思考内容)的最大 token 数;思考会占用此预算',
'llm.maxTokens.minError': '最小值为 256',
'llm.maxTokens.exceedCap': '当前模型输出上限为 {{cap}},保存后将按模型上限生效(配置值被钳制)',
'llm.maxTokens.capInfo': '当前模型输出上限:{{cap}} tokens',
'llm.thinking.unsupported': '当前模型不支持思考模式:请求将自动关闭思考(避免思考耗尽输出预算)',
'llm.pull.title': '下载 Ollama 模型',
'llm.pull.placeholder': '模型名,如 qwen3:8b',
'llm.pull.download': '下载',
@@ -397,6 +441,17 @@ registerTranslations('zh-CN', {
'settings.logs.data.memories': '所有记忆',
'settings.logs.data.auditLogs': '审计日志',
'settings.logs.traceFilesTitle': '会话录制文件(TRACE JSONL',
// v0.8.0 P2-3: 检查更新 / 下载安装
'settings.logs.update.title': '应用更新',
'settings.logs.update.check': '检查更新',
'settings.logs.update.checking': '检查中…',
'settings.logs.update.downloadInstall': '下载并安装',
'settings.logs.update.installing': '准备安装…',
'settings.logs.update.available': '新版本 {{version}} 可用,点击"下载并安装"升级',
'settings.logs.update.upToDate': '已是最新版本({{version}}',
'settings.logs.update.disabled': '未配置更新源(app.updateFeedUrl',
'settings.logs.update.error': '更新失败:{{message}}',
'settings.logs.update.downloaded': '下载完成,即将重启安装…',
'settings.logs.traceStats':
'{{count}} 个文件 · 共 {{size}}(保留策略:最近 200 个,启动时自动清理)',
'settings.logs.statsLoading': '统计中...',
@@ -442,6 +497,10 @@ registerTranslations('zh-CN', {
'settings.mcp.addFailed': '添加 MCP 服务失败',
'settings.mcp.addFailedMsg': '添加 MCP 服务失败:{{message}}',
'settings.mcp.removeFailed': '移除失败',
// v0.8.0 P2-5: MCP Resources / Prompts 发现
'settings.mcp.contents.show': '查看资源与提示词',
'settings.mcp.contents.hide': '收起资源与提示词',
'settings.mcp.contents.empty': '该 Server 未声明 Resources / Prompts 能力',
'settings.mcp.removeFailedMsg': '移除失败:{{message}}',
'settings.mcp.toggleFailed': '操作失败',
'settings.mcp.toggleFailedMsg': '操作失败:{{message}}',
@@ -666,10 +725,44 @@ registerTranslations('en-US', {
'agent.system.stopped': '⏹ Session stopped: {{reason}}',
'agent.error.generic': 'Error: {{message}}',
'agent.error.content_filtered': '⚠️ {{message}}',
// v0.8.0 P2-4: @ file mention
'input.mention.failed': '@{{path}} read failed: {{reason}}',
// ===== v0.8.0 P0-2/P0-4: stream-end contract notices =====
'agent.terminated.output_truncated':
'⚠️ The answer may have been truncated by the output token limit (finish_reason=length). Raise "Max Output Tokens" in Settings → LLM, or lower thinking effort',
'agent.error.output_length_exceeded':
'⚠️ The model consumed its entire output token budget on reasoning and produced no answer (finish_reason=length). Try: raise "Max Output Tokens" / disable or lower thinking / switch to a model with a larger output budget',
'agent.error.empty_response':
'⚠️ The model returned no content (after retries). This may be an upstream drop or output-limit cutoff — retry, or adjust output limit / thinking settings',
'agent.system.empty_completed':
'⚠️ The model returned no content. Please retry, or check the output limit / thinking settings',
'agent.system.finalize_missing':
'⚠️ The run ended without a normal completion event — input restored (results reflect the database)',
'agent.system.abort_failed':
'⚠️ Abort request failed — the agent may still be running; live display restored',
'agent.system.replay_truncated':
' (Note: many events occurred in the background; only the most recent were restored)',
'agent.system.tool_interrupted': 'Interrupted',
'common.cancel': 'Cancel',
'common.delete': 'Delete',
'common.refresh': 'Refresh',
'common.close': 'Close',
// v0.8.0 P2-2: Session replay
'replay.title': 'Session replay',
'replay.file': 'Select recording',
'replay.play': 'Play',
'replay.pause': 'Pause',
'replay.stepBack': 'Step back',
'replay.stepForward': 'Step forward',
'replay.speed': 'Playback speed',
'replay.loadFailed': 'Failed to load recording',
'replay.noRecordings': 'No TRACE recordings for this session yet (logs/session_*.jsonl)',
'replay.skippedLines': '{{count}} corrupt/truncated line(s) skipped',
'replay.event.iterationStart': '▶ Iteration {{iteration}} start',
'replay.event.iterationEnd': '■ Iteration {{iteration}} end ({{duration}})',
'replay.event.sessionEnd': 'Session ended: {{reason}} ({{iterations}} iterations)',
'confirm.title': 'Tool Execution Confirmation',
'confirm.parallelSuffix': ' ({{count}} parallel requests)',
@@ -712,6 +805,13 @@ registerTranslations('en-US', {
'sidebar.noMatch': 'No matches',
'sidebar.empty': 'No sessions yet',
'sidebar.archived': 'Archived',
// v0.8.0 P2-1: Trash
'sidebar.trash': 'Trash (auto-purged after 30 days)',
'sidebar.trash.empty': 'Trash is empty',
'sidebar.trash.purge': 'Delete forever',
'sidebar.trash.purgeTitle': 'Delete session forever?',
'sidebar.trash.purgeBody':
'"{{title}}" and all of its messages will be permanently deleted. This cannot be undone.',
'sidebar.count': '{{count}}',
'sidebar.restore': 'Restore',
'sidebar.deleteTitle': 'Delete Session',
@@ -806,6 +906,16 @@ registerTranslations('en-US', {
'llm.ctxWindow.mimoHelper': 'Default 1000000 (1M), used for compression decisions',
'llm.ctxWindow.oaHelper': 'gpt-4o defaults to 128K, gpt-4.1 to 1M',
'llm.ctxWindow.anthropicHelper': 'Claude defaults to 200K',
// v0.8.0 FEAT-1: max output tokens (llm.maxTokens, sent per request)
'llm.maxTokens.label': 'Max output tokens',
'llm.maxTokens.helper':
'Maximum tokens per response (reasoning included); thinking consumes this budget',
'llm.maxTokens.minError': 'Minimum is 256',
'llm.maxTokens.exceedCap':
'This model caps output at {{cap}} — the saved value will be clamped to the model limit',
'llm.maxTokens.capInfo': 'Model output cap: {{cap}} tokens',
'llm.thinking.unsupported':
'This model does not support thinking: requests will disable thinking automatically (to avoid burning the output budget on reasoning)',
'llm.pull.title': 'Download Ollama model',
'llm.pull.placeholder': 'Model name, e.g. qwen3:8b',
'llm.pull.download': 'Download',
@@ -1048,6 +1158,18 @@ registerTranslations('en-US', {
'settings.logs.data.memories': 'all memories',
'settings.logs.data.auditLogs': 'audit logs',
'settings.logs.traceFilesTitle': 'Session recording files (TRACE JSONL)',
// v0.8.0 P2-3: App update
'settings.logs.update.title': 'App update',
'settings.logs.update.check': 'Check for updates',
'settings.logs.update.checking': 'Checking…',
'settings.logs.update.downloadInstall': 'Download & install',
'settings.logs.update.installing': 'Preparing to install…',
'settings.logs.update.available':
'Version {{version}} is available — click "Download & install" to upgrade',
'settings.logs.update.upToDate': 'Up to date ({{version}})',
'settings.logs.update.disabled': 'No update feed configured (app.updateFeedUrl)',
'settings.logs.update.error': 'Update failed: {{message}}',
'settings.logs.update.downloaded': 'Download complete — restarting to install…',
'settings.logs.traceStats':
'{{count}} file(s) · {{size}} total (retention: last 200, auto-cleaned on startup)',
'settings.logs.statsLoading': 'Computing...',
@@ -1093,6 +1215,10 @@ registerTranslations('en-US', {
'settings.mcp.addFailed': 'Failed to add MCP server',
'settings.mcp.addFailedMsg': 'Failed to add MCP server: {{message}}',
'settings.mcp.removeFailed': 'Remove failed',
// v0.8.0 P2-5: MCP discovery
'settings.mcp.contents.show': 'Show resources & prompts',
'settings.mcp.contents.hide': 'Hide resources & prompts',
'settings.mcp.contents.empty': 'This server does not declare Resources / Prompts capabilities',
'settings.mcp.removeFailedMsg': 'Remove failed: {{message}}',
'settings.mcp.toggleFailed': 'Operation failed',
'settings.mcp.toggleFailedMsg': 'Operation failed: {{message}}',