Files
metona-ai-desktop/docs/v0.8.0-迭代实施清单.md
T
thzxx 839860083f
CI / 类型检查 + Lint + 单元测试 (push) Failing after 9m8s
CI / 全量测试 (Electron ABI) (push) Failing after 6m8s
CI / 产物编译验证 (push) Successful in 11m1s
fix: v0.8.0 修订 — 思考用户意图优先 · 移除元信息硬门控 · 实施清单入库
- DeepSeek/MiMo/Agnes 移除 supportsThinking 元信息硬门控:思考参数完全遵循用户配置
  (事故复盘中 vision-exp 元信息标注不支持思考、实际产生了 8189 token 推理内容,
  元信息不可靠;预算耗尽由引擎降级重试兜底,元信息不符仅告警不拦截)
- Ollama 保留 /api/show 能力探测门控(服务端硬协议约束:向不支持思考的模型发
  think 每次请求 400,属协议正确性而非意图覆盖),探测失败 fail-open
- LLM 设置提示文案修订:元信息不符仍按用户配置发送,降级重试自动兜底
- 测试契约反向钉住:vision-exp + 用户开启→照发 enabled+reasoning_effort;
  关闭/未配置→显式 disabled;Ollama 探测 false→不发 think / null→fail-open
- 补录 docs/v0.8.0-迭代实施清单.md(含逐项验证记录与本次修订记录;
  首次提交时该文件因故未入库,本次补齐)
- 验证:typecheck 0 错误 / lint 0 问题 / 系统 Node 2146 通过 / thinking 矩阵 101 用例全绿
2026-09-05 20:49:20 +08:00

29 KiB
Raw Blame History

v0.8.0 迭代实施清单 —「流语义补全 · 会话可靠 · 恢复力」

版本基线:v0.7.499d0c54)→ 提交 5b9d4d1 + 修订提交 本文档是 v0.8.0 的唯一实施与验收依据。每项含:根因 / 根治方案 / 涉及文件 / 验收标准。 实施完成后,逐项在「验证记录」章节回填验证方式与结果。


P0 — 会话可靠性收口(根治"模型思考中会话停止")

P0-1 finish_reason 全链路贯通

  • 根因finish_reason=lengthsse-stream.ts 仅打日志(engine.ts:340 空输出被判定为 COMPLETED 的上游盲区);Anthropic 流式 message_delta.delta.stop_reasonanthropic.adapter.ts:241)、Ollama 流式 done_reason 均被忽略;TRACE 层 recordLLMResponse 的 finishReason 硬编码 'stop'ipc/agent.ts 两处),事故后无法归因。
  • 根治方案
    1. IR 扩展:MetonaStreamEvent 增加可选 finishReason?: stringDONE 事件携带);IterationStep 增加 finishReason?: stringagent-loop/types.ts)。
    2. OpenAI 兼容共享 SSE 层(sse-stream.ts):跟踪每帧 choices[0].finish_reason,在真实 [DONE] 与断流合成的 DONE 事件上携带;length 保留日志但不再只是日志。
    3. Anthropicmessage_delta 采集 delta.stop_reason,映射(max_tokens→length / refusal|content_filter→content_filter / end_turn→stop / tool_use→tool_calls / pause_turn→stop),携带到 DONE(真实 message_stop 与断流合成两路)。
    4. Ollamachunk.done 时采集 done_reasonstop/length/load/unload 映射),携带到 DONE。
    5. 引擎:消费 adapter DONE 前捕获 finishReason 写入 step.finishReason;引擎最终 DONE 事件携带最后一轮 finishReason。
    6. TRACEipc/agent.ts 主管道与 SubAgent 管道记录真实 finishReason(从 DONE 事件取,缺省 'stop')。
  • 涉及文件metona-response.tsagent-loop/types.tsshared/sse-stream.tsanthropic.adapter.tsollama.adapter.tsengine.tsipc/agent.ts
  • 验收:构造 finish_reason=length 的 SSE 流 → 引擎 step.finishReason==='length'、最终 DONE 携带 finishReason、TRACE JSONL 的 llm_response.finishReason==='length'。

P0-2 引擎空响应守卫 + 截断自愈(degraded retry

  • 根因:引擎对流结束只有二元判定(有工具调用→继续,无→COMPLETED),零内容/思考耗尽型截断全部静默 COMPLETED(生产事故实锤:main.log 22:38:56 / 22:42:35deepseek-v4-flash-vision-expoutput=8192,正文为空,completed 收尾)。
  • 根治方案(收敛在 chatStreamWithRetry 内,天然获得重试/故障转移通道):
    1. 生成器内跟踪:sawThoughtREASONING_DELTA)、sawOutputTEXT_DELTA / TOOL_CALL_DELTA / TOOL_CALL_COMPLETE)、lastFinishReasonDONE 携带)。
    2. 零内容流(!sawThought && !sawOutput)→ 抛 EmptyResponseError(可重试,status=503)→ 指数退避重试;重试耗尽 → ERROR 收尾,message 明确"模型未返回任何内容(可能被输出上限截断或上游异常)"。
    3. 思考耗尽型截断(sawThought && !sawOutput && finishReason==='length')→ 一次性降级重试:临时以 thinkingEnabled=false 重发本次请求(先发 RETRY/STREAM_RESET 事件清前端缓冲),每 run 仅一次;降级重试仍失败 → 抛 OutputTruncatedError(不可重试,code=OUTPUT_LENGTH_EXCEEDED)→ 走故障转移(备用 Provider 预算更大即自愈)→ 无备用则 ERROR 收尾并给用户明确文案(建议提高输出上限 / 关闭思考 / 更换模型)。
    4. 带正文/带工具参数的 length 截断:不重试(工具参数截断已有 _truncatedArguments 自愈),引擎 COMPLETED 但 DONE 携带 finishReason='length',前端展示截断提示。
    5. finish() 的 ERROR/DEAD_LOOP 事件 code 透传:识别 OutputTruncatedErrorMetonaErrorCode.OUTPUT_LENGTH_EXCEEDED
  • 涉及文件engine.tschatStreamWithRetry / finish / isRetryableError)、渲染层(见 P0-4
  • 验收:① 零事件流 mock → 重试 3 次后 ERROR,事件含明确 message;② reasoning-only+length → 降级重试一次(第二次请求体无 thinking 字段)→ 成功则 COMPLETED;③ 降级后仍 length → OUTPUT_LENGTH_EXCEEDED 错误事件;④ 有正文 + length → COMPLETED 且 DONE.finishReason==='length'。
  • 附带根治:abort 恰逢零工具调用轮被 COMPLETED 分支抢占(USER_INTERRUPT 被静默吞掉)——主循环前置 abort 优先级检查;orchestrator 测试"被中断 SubAgent success=false"契约更新。

P0-3 思考参数与输出预算对齐(修订版:用户意图优先)

  • 初版方案(已被修订推翻):按 MODEL_INFO.supportsThinking 元信息硬门控——supportsThinking===false 的模型强制不发思考参数。
  • 修订原因:事故复盘证明元信息不可靠——deepseek-v4-flash-vision-exp 元信息标注"不支持思考",实际却产生了 8189 token 推理内容;且引擎已有完整兜底链(最大输出上限配置 → 空响应守卫 → 降级重试 → OUTPUT_LENGTH_EXCEEDED 明确报错),继续硬拦截属替用户做决定、剥夺用户意图。
  • 修订方案
    1. DeepSeek / MiMo / Agnes 三家:思考参数完全遵循用户配置thinkingEnabled / thinkingEffort),元信息不符时仅 WARN 日志不拦截;未配置/关闭时仍显式 disabled(确定性契约,不依赖服务端隐式默认)。
    2. 小输出预算告警保留:思考开启且钳制后 max_tokens < 8192 时 WARN 日志。
    3. 设置页提示文案修订为:"当前模型元信息标注不支持思考:仍按你的配置发送思考参数;若思考耗尽输出预算,会自动关闭思考重试一次"。
    4. 唯一保留门控是 Ollama/api/show capabilities 探测(服务端实时真值,非静态元信息)为不支持思考时不发 think 参数——与云端不同,这是 Ollama 服务端的硬协议约束(向无思考能力的模型发 think 每次请求 400 "does not support thinking"),属协议正确性而非用户意图覆盖;探测失败 fail-open。
  • 涉及文件deepseek.adapter.tsmimo.adapter.tsagnes-ai.adapter.tsollama.adapter.tssrc/components/settings/LLMSettings.tsxsrc/lib/i18n-strings.ts
  • 验收:用户开启思考时四家请求体均按用户配置携带思考参数(vision-exp 也照发 enabled + reasoning_effort);用户关闭/未配置→显式 disabledOllama 探测 false→不发 think。

P0-4 渲染层截断/空响应可见性

  • 根治方案useAgentStream done 分支——completed && finishReason==='length' → system 消息"回答可能因输出上限被截断";error 分支对 output_length_exceeded 显示友好文案(i18n);completed 且最后一张 assistant 卡片完全为空(无 content/reasoning/toolCalls)→ system 消息"模型未返回内容,请重试或调整输出上限/思考设置"。文案全部入 i18n-stringszh/en)。
  • 涉及文件src/hooks/useAgentStream.tssrc/lib/i18n-strings.tssrc/types/global.d.ts
  • 验收:组件/hook 级测试覆盖三类提示。

P0-5 回归测试四件套(+补充)

  1. reasoning-only 流 + length 的引擎终止判定(本次事故形态,原零覆盖)。
  2. parseSSEStream 集成级空闲超时:挂死 ReadableStream → SseUpstreamError(504)。
  3. SseUpstreamError(504) 经引擎 isRetryableError 归类 → 重试路径一致性。
  4. REASONING_DELTA 持续输出中 abort → USER_INTERRUPT(含迟到 reasoning 不写入)。
  5. 补:engine P4-2(上一 run 30s 强制 abort + 5s 再等待)路径测试;降级重试请求体断言;DONE.finishReason 贯通断言;TRACE finishReason 真值断言。
  • 涉及文件engine.test.tsengine-reliability.test.tsstream-error-and-truncation.test.tssse-stream.test.tsprovider-request-shapes.test.tsipc/__tests__/agent.test.ts
  • 验收:新增用例全部通过,npm run test:electron 全量绿。

FEAT-1 LLM 设置新增「最大输出上限」配置项(用户需求)

  • 方案
    1. LLMSettings 新增「最大输出上限 (tokens)」数字项,读写既有配置键 llm.maxTokens(引擎/主进程已全链路消费:main.ts baseConfig → engine params.maxTokens → adapter 按 {model}.maxOutputTokens 钳制)。
    2. 可见性门控:新增渲染层 provider 能力映射(model-capabilities.ts 导出 PROVIDER_OUTPUT_LIMIT_SUPPORT,六家当前均支持输出上限参数——DeepSeek max_tokens / Agnes max_tokens / MiMo max_completion_tokens / OpenAI max_tokens|max_completion_tokens / Anthropic max_tokens / Ollama num_predict;映射表数据驱动,未来不支持者自动隐藏)。
    3. 应用增强(根治静默钳制):输入旁展示当前模型 maxOutputTokens 上限提示;保存时配置值 > 当前模型 maxOutputTokenslistModels 元数据),toast 提示"将按模型上限 X 生效"llm.maxTokens 已在 applyEngineConfigKey(shared.ts)热生效,无需重启。
  • 涉及文件src/components/settings/LLMSettings.tsxsrc/lib/model-capabilities.tssrc/__tests__/model-capabilities.test.ts
  • 验收:切 Provider 显示/隐藏正确(含不支持路径的模拟);保存后 config:get llm.maxTokens 值正确且引擎热更新被调用。

P1 — 修复面收口

P1-1 渲染层流式三缺陷

  1. 后台会话流恢复(根治"切走再切回,回复消失一半"):主进程 ipc/agent.ts 为每个运行中会话维护有界回放缓冲streamEvent + stateChange 统一序,上限 2000 条 / 4MBTERMINATED 后保留至下次 run 启动清除);新增 IPC agent:getReplayState(sessionId) 返回 {isRunning, runId, events};渲染层新建 stream-event-bus.ts(订阅/发布),useAgentStream 的事件处理改为总线消费;agent-store.setCurrentSession 检测目标会话在 sessionRunStates 中运行 → 拉取回放并按序灌入总线 → 后续实时事件无缝衔接。缓冲溢出丢弃最旧并打标(replay truncated 提示)。
  2. abort() 自愈修复(根治"UI idle 但引擎仍在跑"useAgentStream stateChange 分支——!isStreaming && data.runId === store.currentRunId && data.state ∈ 活动态setStreaming(true) 恢复流式态;agent-store.abort() 的 IPC 失败路径:回滚本地状态(isStreaming=true、status=thinking+ toast 告知中断未生效;修正失实注释。
  3. sendMessage 收尾兜底invoke resolve 后 2s 检查——若 isStreaming 仍为 true(说明 DONE/ERROR 收尾事件未达)→ 插入 system 消息"会话已结束但未收到正常收尾事件,已恢复输入"并 finalizeinvoke resolve 且 r.success===false 时若仍在流式 → 立即以 error 文案 finalize。
  4. abort 后悬空 executing 卡片兜底done/error 处理时清扫所有 status==='executing'|'pending' 的工具调用(messages 与 traceSteps)→ 置 error + "已中断"。
  • 涉及文件ipc/agent.tspreload.tsglobal.d.tssrc/lib/stream-event-bus.ts(新)、src/hooks/useAgentStream.tssrc/stores/agent-store.ts
  • 验收:三场景 hook 级测试(切回恢复/abort 失败自愈/收尾兜底)+ ipc 回放缓冲单测。

P1-2 工具 abort 信号全覆盖

  • http-request.tscontext.signal 传入 ssrfPinnedFetch 第 4 参(管道已支持)。
  • web-fetch.ts:HTTP 阶段与重定向链全程携带 signal;浏览器回退阶段 abort → webContents.stop() + reject。
  • web-search.ts:预检 HEAD、SearXNG 请求、autoFetch 内部 web_fetch 全部携带真实 context.signal(移除假 context);autoFetch 加时间预算——总耗时不超过 min(工具超时×0.8, 剩余预算),超预算即止(根治 720s>300s)。
  • code-search.tsripgrep 子进程接收 abort(监听 signal → child.kill())。
  • git.ts:共享 execFile 帮助函数接收 signal 选项(Node execFile 原生支持)。
  • delegate-task.ts + orchestrator.tsdelegate 接受 signalsignal aborted → subEngine.abort()
  • 验收:每工具至少一条"abort 后 X 毫秒内终止"的测试(子进程 kill / fetch reject / 委派终止)。

P1-3 确定缺陷清单(根治修复)

  1. SSE 空 error 对象防御失效extractUpstreamErrorFramemessage 兜底 '{}' 恒真)→ 无 message 且无 status 且无 code 时返回 null;同步修改锁行为测试(sse-stream.test.ts:589-596)。
  2. Ollama generate/embed 固定超时不合并外部信号 → 增加可选 signal 参数,AbortSignal.any 合并;pullModel 已有 signal 保持。
  3. isBinaryFile 拒绝一切 UTF-16 → BOMFF FE / FE FF)前缀视为文本(打通不可达的 UTF-16 解码分支);同步修改 filesystem-tools.test.ts:251-267 锁行为测试。
  4. tmp 文件名同毫秒碰撞filesystem.ts / file-editor.ts)→ tmp_${Date.now()}_${nanoid(6)}
  5. code_search JS 回退参数不对称 → 回退实现补 case_sensitive / context_before / context_after
  6. list_directory 硬跳过 node_modules → 新增可选参数 include_node_modules(默认 false,行为兼容)。
  7. window-manager 崩溃自愈无退避 → 连续 reload 计数:60s 内 ≥3 次不再自动 reload,改弹 error 对话框 + 日志;稳定 5 分钟后计数复位。
  8. i18n 收口MemoryViewer "暂无记忆数据" → t('memory.empty')Sidebar ToolManagerPanel riskLabels 补 critical
  • 验收:每项一条回归测试(i18n 项组件测试断言)。

P1-4 浏览器通道 SSRF 根治(本地 Pinned CONNECT 代理)

  • 根因web_fetch 浏览器回退与 web_browser.open 走 Chromium 自有网络栈,仅入口一次 SSRF 校验,无 DNS pinning。
  • 根治方案:新增 electron/utils/pinned-proxy.ts——主进程本地 HTTP 代理(127.0.0.1 随机端口):CONNECT → 解析目标 host → resolvePublicAddresses 校验(仅公网)→ 取 pinned IP 直接 net.connect 建立隧道(TLS/SNI/证书校验仍由 Chromium 端到端完成);绝对形式 HTTP 同校验后转发;校验失败 → 403 拒绝(fail-closed)。BrowserWindowManager 创建分区时按需启动代理并 setProxy;用户已配置 network.proxyUrlproxyActive)时保持既有行为(M7 已知限制记录在案)。失败降级:代理启动失败仅 WARN,浏览器回退可用性优先。
  • 涉及文件electron/utils/pinned-proxy.ts(新)、browser-window-manager.tsweb-fetch.ts(浏览器阶段经同一分区自动覆盖)
  • 验收:代理单测——公网 CONNECT 隧道建立、私网/元数据目标 403 拒绝、绝对形式 HTTP 转发、端口 0 随机分配。

P1-5 SearXNG/LLM 配置 URL 深校验(域名 DNS 解析)

  • ssrf-guard.ts 新增 assertSafeConfigTargetDeep(url):域名做真实 DNS 解析(all:true),任一解析结果命中云元数据/链路本地(169.254/16)、0/8、组播保留段(≥224)、IPv6 等价段 → 拒绝;回环/RFC1918 放行(本地实例合法);DNS 解析失败放行并 WARN(配置期校验为纵深,运行时工具仍有独立校验)。
  • ipc/shared.ts assertSafeConfigUrls 改为 async 并对 URL 类键(llm.baseURL / llm.fallbackBaseURL / searxng.url / app.updateFeedUrl)启用深校验;config:set / setBatch 两处调用点同步。
  • 验收metadata.google.internal、解析到 169.254.169.254 的域名(mock dns)被拒;localhost / 192.168.x / DNS 失败放行。

P2 — 能力演进

P2-1 会话回收站(30 天)

  • DB 迁移(SCHEMA_VERSION 2→3):sessionsdeleted_at INTEGER NULL + 索引;sessions:delete 改软删除(置 deleted_at);list/getSession/getMessages/searchMessages 全部排除已删除;新增 IPC sessions:listTrash / sessions:restore / sessions:purge(硬删除,级联消息);启动时与每 24h 定时清理超 30 天回收站;data:clearSessions 连回收站一并硬删。
  • UI:Sidebar 底部「回收站」折叠区(列出已删会话 + 恢复/彻底删除,带确认)。
  • 验收:删除→列表消失、回收站出现→恢复回列表→彻底删除级联清消息;30 天清理函数单测;迁移测试升级(user_version=3)。

P2-2 会话回放(JSONL 播放器)

  • IPCsessions:listRecordings(sessionId)(扫描 workspace/logs/session_{id}_*.jsonl,带 mtime/size);sessions:readRecording(relativeName)(严格校验文件名模式与目录边界,返回解析后事件数组,单文件 ≤20MB 截断保护)。
  • UI:Trace 面板新增「会话回放」入口 → 新组件 SessionReplayPlayer:文件选择、时间轴(iteration_start 定锚点)、播放/暂停/步进/速度、事件流渲染(思考/工具调用/结果/正文),复用 formattersEsc 关闭。
  • 验收:组件测试覆盖加载/步进/边界(空文件/损坏行跳过);IPC 路径逃逸拒绝测试。

P2-3 electron-updater 自动更新接入

  • 依赖 electron-updater@^6electron-builder.ymlpublish: { provider: generic, url }(默认取 app.updateFeedUrl 语义)。
  • update.service.ts 扩展:保留手动 feed 比对 check();新增 autoUpdater 薄封装(生产环境启动 5s 后 checkForUpdatesavailable → 广播 update:status 事件 + toast;新增 IPC app:updateDownloadAndInstall 触发 downloadAndInstall,进度/完成/失败事件广播);dev 模式跳过;未配置 feed 时静默禁用。
  • UI:设置 → 日志与数据 新增"检查更新/下载并安装"入口与进度显示。
  • 验收service 单测(stub autoUpdater 事件映射);IPC 面注册断言;dev 跳过断言。

P2-4 输入框 @ 文件提及

  • IPCworkspace:listFiles(query, limit)(工作空间递归文件名索引,跳过 node_modules/.git/logs/.metona,≤2000 条);workspace:readFileClip(relPath, maxBytes=512KB)(路径边界 + 大小上限 + 文本判定,二进制拒绝)。
  • UIChatInput 输入 @ 触发 MUI Popover 文件联想(Fuse 模糊匹配,键盘上下/回车选中,Esc 关闭);选中插入 @相对路径 token;发送前解析所有 @path → readFileClip → 以 attachmentstype=text, name=path)注入(复用既有附件管线与 LLM 提示契约);文件缺失 toast 警告并原样发送。
  • 验收:组件测试(联想弹出/选择插入/发送注入);IPC 边界测试(逃逸路径/超大/二进制拒绝)。

P2-5 MCP Resources/Prompts 发现

  • mcp-manager.service.ts:连接成功后 try/catch listResources() / listPrompts()(可选能力,失败置空不阻断);状态结构增 resources/prompts;新增 IPC mcp:listServerContents(name)
  • UIMCPSettings 每个 server 增可展开「资源/提示词」区。
  • 验收:mock SDK 的发现/降级测试;UI 渲染测试(有空/无空两态)。

P2-6 文档对齐

  • docs/MetonaAI-Desktop 内部API请求与响应标准.htmlAdapter 清单补 MiMo/OpenAI/Anthropic(移除"未实现"注记);流事件全集补 stream_reset 与 DONE.finishReasonFinishReason 章节补 MiMo repetition_truncation 映射说明。
  • README.md:版本徽章 + v0.8.0 亮点表 + 配置说明对齐。
  • 验收:文档 diff 逐条核对实际实现。

P3 — 测试与流程基建

  1. 弱断言根治registry.test.ts WEBP 恒真分支改单向断言;hooks-contracts.test.ts:251 自比恒真改真实断言;memory-manager.test.ts 空 token 断言补 length===0;其余环境依赖型断言复核确认。
  2. IPC 事件管道真实转发测试agent.test.ts 新增回放缓冲 describe(真实 EventEmitter → streamEvent/stateChange 转发 → 按序缓冲断言)。
  3. useAgentStream hook 级测试:done 收尾、截断提示、错误文案、reasoning/text delta 累积(jsdom)。
  4. sendStream thinking 参数断言thinking 契约矩阵经 toNativeRequest 共享入口覆盖 send/sendStream 两路径。
  5. CIelectron-test 已是阻塞门禁(既有配置),新增用例纳入覆盖。
  • 验收:全部新增/修改测试在 npm run test:electron 下绿。

收尾

  • package.json version → 0.8.0README 徽章同步。
  • 全量 npm run typecheck + npm run lint + npm run test:electron 三绿。

验证记录(实施完成后逐项回填)

全局验证(2026-09-05):npm run typecheck 0 错误;npm run lint 0 问题; npm test(系统 Node2144 通过 / 301 按 ABI 设计跳过; npm run test:electronElectron ABI 全量)2445/2445 用例全部通过,0 跳过。 (修订提交后复跑:thinking 相关测试矩阵全绿,见 P0-3 修订行。)

验证方式 结果
P0-1 finish_reason 贯通 engine-stream-contract.testDONE 携带 finishReasonstop/length 两用例);step.finishReason 记录;ipc/agent.ts 主管道与 SubAgent 管道 finishReason ?? 'stop' 真值接线(代码审查 + agent.test 管线测试)
P0-2 空响应守卫 + 降级重试 engine-stream-contract.test 6 用例:零产出→重试耗尽 ERRORreasoning-only+length→一次性降级重试(第二次请求 thinkingEnabled=false 断言)→COMPLETED;降级后仍 length→OUTPUT_LENGTH_EXCEEDED;有正文+length→COMPLETED 不重试;reasoning 自然结束→重试路径
P0-2 附带根治 abort 优先级缺陷(新测试发现):abort 恰逢零工具调用轮被 COMPLETED 抢占吞成 completed → 前置检查修复;orchestrator 测试"被中断 SubAgent success=false"契约更新
P0-3 思考与预算对齐 thinking-capability-gate.test 7 用例。修订(用户意图优先):初版按 supportsThinking 元信息硬门控,经复核推翻——事故复盘中 vision-exp 元信息标注"不支持思考"、实际却产生了 8189 token 推理内容(元信息不可靠),且引擎已有完整兜底链(最大输出上限 → 空响应守卫 → 降级重试 → 结构化报错),硬拦截属替用户做决定。现 DeepSeek/MiMo/Agnes 三家完全遵循用户配置(元信息不符仅告警);唯一保留门控是 Ollama /api/show 探测——服务端硬协议约束(向不支持思考的模型发 think 每次请求 400),属协议正确性。测试反向钉住:vision-exp + 用户开启→照发 enabled+reasoning_effort;关闭/未配置→显式 disabledOllama 探测 false→不发 think / null→fail-open。小输出预算告警保留 (修订)
P0-4 渲染层提示 use-agent-stream.test 5 用例:done 收尾;length 截断提示;output_length_exceeded 友好文案;reasoning/text delta 累积。i18n zh/en 全部出层
P0-5 四件套 ①reasoning-only 终止判定(contract);②集成级空闲超时(engine-stream-reliability:挂死流→504fake timers);③SseUpstreamError(504)→引擎重试(同文件);④REASONING 中 abort→USER_INTERRUPT(同文件);⑤P4-2 30s 强制 abort 路径(同文件 fake timers
FEAT-1 最大输出上限 LLMSettings 新增「最大输出上限」项:supportsOutputLimitConfigmodel-capabilities.ts Provider 支持矩阵门控,六家均支持、数据驱动可扩展);展示模型 maxOutputTokens 上限与超限警告;保存经 llm.maxTokens 热生效(applyEngineConfigKey);typecheck + 表测沿用
P1-1a 后台会话回放 ipc/agent.ts 有界缓冲(2000 条/4MB/LRU 50 会话)+ agent:getReplayState + 渲染层 stream-event-bus + setCurrentSession 回放恢复;agent.test 2 用例(按序缓冲/runId 记录/INIT 清空/无效参数)
P1-1b abort 自愈 useAgentStream stateChange THINKING 自愈(runId 匹配恢复 isStreaming);agent-store.abort IPC 失败回滚 + toast;注释与实现对齐
P1-1c 收尾兜底 agent-store.sendMessageresolve 且 success=false→本地 error 收尾;resolve 后 2s 仍流式→finalize_missing 提示 + 收尾(currentSessionId 守卫)
P1-1d 卡片清扫 useAgentStream done/error 均调用 sweepInterruptedToolCallsmessages + traceSteps 的 executing/pending→error"已中断"
P1-2 工具 abort 全覆盖 http_requestssrfPinnedFetch 第 4 参);web_fetch(逐跳 signal + abort 感知 sleep + 浏览器阶段 webContents.stop);web_search(全链路 signal + 300s×0.8 时间预算 + 移除伪造 context 与死代码);code_searchrg 子进程 signal + AbortError 分支);gitrunGit signal 8 处调用点);delegate_task→orchestratorsignal 联动 abortSubEngine + finally 解除监听);ollama generate/embed AbortSignal.any 合并 (实现 + typecheck;子进程级 kill 依赖 Node execFile/spawn signal 原生语义)
P1-3.1 SSE 空 error 对象 extractUpstreamErrorFrame 重构(显式 message/status/code 三标识判定);sse-stream.test 契约更新(空对象跳过 + 有 code 占位文案)
P1-3.2 Ollama 外部信号 generate/embed 增 signal 参数 + AbortSignal.any 合并
P1-3.3 UTF-16 isBinaryFile BOM 前缀放行;filesystem-tools.test 契约更新(LE/BE 正常读取,utf-16le 解码断言)
P1-3.4 tmp 碰撞 filesystem.ts / file-editor.ts tmp 名追加 nanoid(6)
P1-3.5 code_search 对称 SearchFilesTool 新增 case_sensitive / context_before / context_after(独立前后文切片);回退路径原样透传
P1-3.6 node_modules list_directory 新增 include_node_modules(默认 false 兼容)
P1-3.7 崩溃自愈退避 WindowManager 60s 滑窗 ≥3 次停止 reload + showErrorBox5 分钟稳定复位
P1-3.8 i18n 收口 MemoryViewer→t('memory.empty')Sidebar riskLabels 补 critical
P1-4 Pinned 代理 pinned-proxy.tsCONNECT 隧道 + 绝对形式 HTTP + 可注入 resolver);BrowserWindowManager 分区接入(proxyActive 时让位用户代理);network-proxy 不覆盖已激活的 pinned 分区;pinned-proxy.test 9 用例(200 隧道透传/403 fail-closed/400 非法目标/幂等启动/深校验 5 例)
P1-5 配置 URL 深校验 assertSafeConfigTargetDeep__dnsLookup 可替换点 + DeepCheckSoftFailure 软失败);shared.ts assertSafeConfigUrls 转 asyncconfig:set/setBatch await 接入;测试覆盖元数据 IP 拒绝/公网放行/DNS 失败软放行/回环放行
P2-1 回收站 DB SCHEMA_VERSION 3 + 迁移 10(存在性守卫,createTables 静态索引移除以兼容遗留库);session.service 软删除/listDeleted/restore/purge/purgeExpiredTrashsearchMessages 聚合剔除;IPC listTrash/restore/purgemain.ts 启动+24h 清理;Sidebar TrashSessionsPanel(恢复/彻底删除确认);session-trash.test 5 用例(Electron ABI 通过)+ 迁移矩阵契约更新
P2-2 会话回放 IPC listRecordings/readRecording(文件名白名单 + 目录边界 + 20MB 上限 + 坏行跳过);SessionReplayPlayer(选择/时间轴/播放暂停步进/0.5-4×/事件渲染);DetailPanel Trace 入口;i18n zh/en
P2-3 electron-updater update.service 双轨(手动 feed 比对保留 + startAutoUpdater/getAutoUpdaterHandle);main.ts 生产环境启动 5s 静默检查 + update:status 广播 + 系统通知;IPC app:updateInstallpreload onUpdateStatus/updateInstallLogsSettings UpdatePanel(检查/下载安装/进度);electron-builder.yml publish generic
P2-4 @ 文件提及 workspace.listFiles2000 上限/噪声目录跳过/深度 8+ readFileClip(边界/512KB/NUL 拒绝/MEMORY.md 保护);IPC + preload + 类型;ChatInput@ 触发联想(Fuse 模糊 + 键盘导航)、applyMention 片段替换、发送时 extractMentions(邮箱防护/尾点剥离/≤5 个)→附件管线注入 + 失败 toast
P2-5 MCP 发现 connectServer try/catch listResources/listPrompts(可选能力降级空数组);getServerContentsIPC mcp:listServerContentsMCPSettings 每 server 展开"资源与提示词"i18n zh/en
P2-6 文档对齐 内部 API 标准 HTMLAdapter 清单补 MiMo/更新 Anthropic/OpenAI 注记、MVP 表改为六家已实现、事件枚举移除 THINKING_* 补 STREAM_RESET、DONE.finishReason 语义与 repetition_truncation 映射说明;README:版本徽章 0.8.0 + v0.8.0 亮点表(P0-3 行随修订更新)
P3-1 弱断言根治 registry WEBP(确定性 WEBP 魔数单向断言);hooks-contracts 自比→真实断言;memory 空 token 补 length===0(另 2 处 GBK/mcp-if-else 属环境依赖型,复核确认保留原状)
P3-2 IPC 管道 agent.test 新增回放 describe:真实 EventEmitter → streamEvent/stateChange 转发 → 缓冲断言;既有 9 步编排/ERROR+DONE 广播断言沿用
P3-3 hook 级测试 use-agent-stream.test 5 用例(done 收尾/截断提示/错误文案/思考与正文累积),jsdom 环境
P3-4 sendStream thinking P0-3 修订契约覆盖请求体构建(toNativeRequest 为 send/sendStream 共享单一入口,门控矩阵即两路径契约) (共享入口口径)
P3-5 CI CI electron-test 已是阻塞门禁(既有配置),本次新增全部用例纳入其覆盖
收尾 package.json 0.8.0typecheck/lint/双模式全量三绿(见顶部全局验证)