docs: 删除 v0.8.0 / v0.8.1 迭代实施清单(验收留档已随发行版归档)
This commit is contained in:
@@ -1,208 +0,0 @@
|
|||||||
# v0.8.0 迭代实施清单 —「流语义补全 · 会话可靠 · 恢复力」
|
|
||||||
|
|
||||||
> 版本基线:v0.7.4(99d0c54)→ 提交 5b9d4d1 + 修订提交
|
|
||||||
> 本文档是 v0.8.0 的**唯一实施与验收依据**。每项含:根因 / 根治方案 / 涉及文件 / 验收标准。
|
|
||||||
> 实施完成后,逐项在「验证记录」章节回填验证方式与结果。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## P0 — 会话可靠性收口(根治"模型思考中会话停止")
|
|
||||||
|
|
||||||
### P0-1 finish_reason 全链路贯通
|
|
||||||
- **根因**:`finish_reason=length` 在 `sse-stream.ts` 仅打日志(engine.ts:340 空输出被判定为 COMPLETED 的上游盲区);Anthropic 流式 `message_delta.delta.stop_reason`(anthropic.adapter.ts:241)、Ollama 流式 `done_reason` 均被忽略;TRACE 层 `recordLLMResponse` 的 finishReason 硬编码 `'stop'`(ipc/agent.ts 两处),事故后无法归因。
|
|
||||||
- **根治方案**:
|
|
||||||
1. IR 扩展:`MetonaStreamEvent` 增加可选 `finishReason?: string`(DONE 事件携带);`IterationStep` 增加 `finishReason?: string`(agent-loop/types.ts)。
|
|
||||||
2. OpenAI 兼容共享 SSE 层(sse-stream.ts):跟踪每帧 `choices[0].finish_reason`,在真实 `[DONE]` 与断流合成的 DONE 事件上携带;`length` 保留日志但不再只是日志。
|
|
||||||
3. Anthropic:`message_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. Ollama:`chunk.done` 时采集 `done_reason`(stop/length/load/unload 映射),携带到 DONE。
|
|
||||||
5. 引擎:消费 adapter DONE 前捕获 finishReason 写入 `step.finishReason`;引擎最终 DONE 事件携带最后一轮 finishReason。
|
|
||||||
6. TRACE:ipc/agent.ts 主管道与 SubAgent 管道记录真实 finishReason(从 DONE 事件取,缺省 'stop')。
|
|
||||||
- **涉及文件**:`metona-response.ts`、`agent-loop/types.ts`、`shared/sse-stream.ts`、`anthropic.adapter.ts`、`ollama.adapter.ts`、`engine.ts`、`ipc/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:35,deepseek-v4-flash-vision-exp,output=8192,正文为空,completed 收尾)。
|
|
||||||
- **根治方案**(收敛在 `chatStreamWithRetry` 内,天然获得重试/故障转移通道):
|
|
||||||
1. 生成器内跟踪:`sawThought`(REASONING_DELTA)、`sawOutput`(TEXT_DELTA / TOOL_CALL_DELTA / TOOL_CALL_COMPLETE)、`lastFinishReason`(DONE 携带)。
|
|
||||||
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 透传:识别 `OutputTruncatedError` → `MetonaErrorCode.OUTPUT_LENGTH_EXCEEDED`。
|
|
||||||
- **涉及文件**:`engine.ts`(chatStreamWithRetry / 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.ts`、`mimo.adapter.ts`、`agnes-ai.adapter.ts`、`ollama.adapter.ts`、`src/components/settings/LLMSettings.tsx`、`src/lib/i18n-strings.ts`
|
|
||||||
- **验收**:用户开启思考时四家请求体均按用户配置携带思考参数(vision-exp 也照发 enabled + reasoning_effort);用户关闭/未配置→显式 disabled;Ollama 探测 false→不发 think。
|
|
||||||
|
|
||||||
### P0-4 渲染层截断/空响应可见性
|
|
||||||
- **根治方案**:`useAgentStream` done 分支——`completed && finishReason==='length'` → system 消息"回答可能因输出上限被截断";error 分支对 `output_length_exceeded` 显示友好文案(i18n);`completed` 且最后一张 assistant 卡片完全为空(无 content/reasoning/toolCalls)→ system 消息"模型未返回内容,请重试或调整输出上限/思考设置"。文案全部入 i18n-strings(zh/en)。
|
|
||||||
- **涉及文件**:`src/hooks/useAgentStream.ts`、`src/lib/i18n-strings.ts`、`src/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.ts`、`engine-reliability.test.ts`、`stream-error-and-truncation.test.ts`、`sse-stream.test.ts`、`provider-request-shapes.test.ts`、`ipc/__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` 上限提示;保存时配置值 > 当前模型 maxOutputTokens(listModels 元数据),toast 提示"将按模型上限 X 生效";`llm.maxTokens` 已在 `applyEngineConfigKey`(shared.ts)热生效,无需重启。
|
|
||||||
- **涉及文件**:`src/components/settings/LLMSettings.tsx`、`src/lib/model-capabilities.ts`、`src/__tests__/model-capabilities.test.ts`
|
|
||||||
- **验收**:切 Provider 显示/隐藏正确(含不支持路径的模拟);保存后 `config:get llm.maxTokens` 值正确且引擎热更新被调用。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## P1 — 修复面收口
|
|
||||||
|
|
||||||
### P1-1 渲染层流式三缺陷
|
|
||||||
1. **后台会话流恢复(根治"切走再切回,回复消失一半")**:主进程 ipc/agent.ts 为每个运行中会话维护**有界回放缓冲**(streamEvent + stateChange 统一序,上限 2000 条 / 4MB,TERMINATED 后保留至下次 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 消息"会话已结束但未收到正常收尾事件,已恢复输入"并 finalize;invoke resolve 且 `r.success===false` 时若仍在流式 → 立即以 error 文案 finalize。
|
|
||||||
4. **abort 后悬空 executing 卡片兜底**:done/error 处理时清扫所有 `status==='executing'|'pending'` 的工具调用(messages 与 traceSteps)→ 置 `error` + "已中断"。
|
|
||||||
- **涉及文件**:`ipc/agent.ts`、`preload.ts`、`global.d.ts`、`src/lib/stream-event-bus.ts`(新)、`src/hooks/useAgentStream.ts`、`src/stores/agent-store.ts`
|
|
||||||
- **验收**:三场景 hook 级测试(切回恢复/abort 失败自愈/收尾兜底)+ ipc 回放缓冲单测。
|
|
||||||
|
|
||||||
### P1-2 工具 abort 信号全覆盖
|
|
||||||
- `http-request.ts`:`context.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.ts`:ripgrep 子进程接收 abort(监听 signal → child.kill())。
|
|
||||||
- `git.ts`:共享 execFile 帮助函数接收 `signal` 选项(Node execFile 原生支持)。
|
|
||||||
- `delegate-task.ts` + `orchestrator.ts`:delegate 接受 `signal`,signal aborted → `subEngine.abort()`。
|
|
||||||
- **验收**:每工具至少一条"abort 后 X 毫秒内终止"的测试(子进程 kill / fetch reject / 委派终止)。
|
|
||||||
|
|
||||||
### P1-3 确定缺陷清单(根治修复)
|
|
||||||
1. **SSE 空 error 对象防御失效**(extractUpstreamErrorFrame:message 兜底 `'{}'` 恒真)→ 无 message 且无 status 且无 code 时返回 null;同步修改锁行为测试(sse-stream.test.ts:589-596)。
|
|
||||||
2. **Ollama generate/embed 固定超时不合并外部信号** → 增加可选 `signal` 参数,`AbortSignal.any` 合并;pullModel 已有 signal 保持。
|
|
||||||
3. **isBinaryFile 拒绝一切 UTF-16** → BOM(FF 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.proxyUrl`(proxyActive)时保持既有行为(M7 已知限制记录在案)。失败降级:代理启动失败仅 WARN,浏览器回退可用性优先。
|
|
||||||
- **涉及文件**:`electron/utils/pinned-proxy.ts`(新)、`browser-window-manager.ts`、`web-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):`sessions` 增 `deleted_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 播放器)
|
|
||||||
- IPC:`sessions:listRecordings(sessionId)`(扫描 workspace/logs/session_{id}_*.jsonl,带 mtime/size);`sessions:readRecording(relativeName)`(严格校验文件名模式与目录边界,返回解析后事件数组,单文件 ≤20MB 截断保护)。
|
|
||||||
- UI:Trace 面板新增「会话回放」入口 → 新组件 `SessionReplayPlayer`:文件选择、时间轴(iteration_start 定锚点)、播放/暂停/步进/速度、事件流渲染(思考/工具调用/结果/正文),复用 formatters;Esc 关闭。
|
|
||||||
- **验收**:组件测试覆盖加载/步进/边界(空文件/损坏行跳过);IPC 路径逃逸拒绝测试。
|
|
||||||
|
|
||||||
### P2-3 electron-updater 自动更新接入
|
|
||||||
- 依赖 `electron-updater@^6`;`electron-builder.yml` 增 `publish: { provider: generic, url }`(默认取 app.updateFeedUrl 语义)。
|
|
||||||
- `update.service.ts` 扩展:保留手动 feed 比对 `check()`;新增 `autoUpdater` 薄封装(生产环境启动 5s 后 `checkForUpdates`,available → 广播 `update:status` 事件 + toast;新增 IPC `app:updateDownloadAndInstall` 触发 downloadAndInstall,进度/完成/失败事件广播);dev 模式跳过;未配置 feed 时静默禁用。
|
|
||||||
- UI:设置 → 日志与数据 新增"检查更新/下载并安装"入口与进度显示。
|
|
||||||
- **验收**:service 单测(stub autoUpdater 事件映射);IPC 面注册断言;dev 跳过断言。
|
|
||||||
|
|
||||||
### P2-4 输入框 @ 文件提及
|
|
||||||
- IPC:`workspace:listFiles(query, limit)`(工作空间递归文件名索引,跳过 node_modules/.git/logs/.metona,≤2000 条);`workspace:readFileClip(relPath, maxBytes=512KB)`(路径边界 + 大小上限 + 文本判定,二进制拒绝)。
|
|
||||||
- UI:ChatInput 输入 `@` 触发 MUI Popover 文件联想(Fuse 模糊匹配,键盘上下/回车选中,Esc 关闭);选中插入 `@相对路径` token;发送前解析所有 `@path` → readFileClip → 以 attachments(type=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)`。
|
|
||||||
- UI:MCPSettings 每个 server 增可展开「资源/提示词」区。
|
|
||||||
- **验收**:mock SDK 的发现/降级测试;UI 渲染测试(有空/无空两态)。
|
|
||||||
|
|
||||||
### P2-6 文档对齐
|
|
||||||
- `docs/MetonaAI-Desktop 内部API请求与响应标准.html`:Adapter 清单补 MiMo/OpenAI/Anthropic(移除"未实现"注记);流事件全集补 `stream_reset` 与 DONE.finishReason;FinishReason 章节补 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. **CI**:electron-test 已是阻塞门禁(既有配置),新增用例纳入覆盖。
|
|
||||||
- **验收**:全部新增/修改测试在 `npm run test:electron` 下绿。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 收尾
|
|
||||||
|
|
||||||
- `package.json` version → 0.8.0;README 徽章同步。
|
|
||||||
- 全量 `npm run typecheck` + `npm run lint` + `npm run test:electron` 三绿。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 验证记录(实施完成后逐项回填)
|
|
||||||
|
|
||||||
> 全局验证(2026-09-05):`npm run typecheck` 0 错误;`npm run lint` 0 问题;
|
|
||||||
> `npm test`(系统 Node)2144 通过 / 301 按 ABI 设计跳过;
|
|
||||||
> `npm run test:electron`(Electron ABI 全量)**2445/2445 用例全部通过,0 跳过**。
|
|
||||||
> (修订提交后复跑:thinking 相关测试矩阵全绿,见 P0-3 修订行。)
|
|
||||||
|
|
||||||
| 项 | 验证方式 | 结果 |
|
|
||||||
|---|---|---|
|
|
||||||
| P0-1 finish_reason 贯通 | engine-stream-contract.test:DONE 携带 finishReason(stop/length 两用例);step.finishReason 记录;ipc/agent.ts 主管道与 SubAgent 管道 `finishReason ?? 'stop'` 真值接线(代码审查 + agent.test 管线测试) | ✅ |
|
|
||||||
| P0-2 空响应守卫 + 降级重试 | engine-stream-contract.test 6 用例:零产出→重试耗尽 ERROR;reasoning-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;关闭/未配置→显式 disabled;Ollama 探测 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:挂死流→504,fake timers);③SseUpstreamError(504)→引擎重试(同文件);④REASONING 中 abort→USER_INTERRUPT(同文件);⑤P4-2 30s 强制 abort 路径(同文件 fake timers) | ✅ |
|
|
||||||
| FEAT-1 最大输出上限 | LLMSettings 新增「最大输出上限」项:`supportsOutputLimitConfig`(model-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.sendMessage:resolve 且 success=false→本地 error 收尾;resolve 后 2s 仍流式→finalize_missing 提示 + 收尾(currentSessionId 守卫) | ✅ |
|
|
||||||
| P1-1d 卡片清扫 | useAgentStream done/error 均调用 sweepInterruptedToolCalls(messages + traceSteps 的 executing/pending→error"已中断") | ✅ |
|
|
||||||
| P1-2 工具 abort 全覆盖 | http_request(ssrfPinnedFetch 第 4 参);web_fetch(逐跳 signal + abort 感知 sleep + 浏览器阶段 webContents.stop);web_search(全链路 signal + 300s×0.8 时间预算 + 移除伪造 context 与死代码);code_search(rg 子进程 signal + AbortError 分支);git(runGit signal 8 处调用点);delegate_task→orchestrator(signal 联动 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 + showErrorBox;5 分钟稳定复位 | ✅ |
|
|
||||||
| P1-3.8 i18n 收口 | MemoryViewer→t('memory.empty');Sidebar riskLabels 补 critical | ✅ |
|
|
||||||
| P1-4 Pinned 代理 | pinned-proxy.ts(CONNECT 隧道 + 绝对形式 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 转 async,config:set/setBatch await 接入;测试覆盖元数据 IP 拒绝/公网放行/DNS 失败软放行/回环放行 | ✅ |
|
|
||||||
| P2-1 回收站 | DB SCHEMA_VERSION 3 + 迁移 10(存在性守卫,createTables 静态索引移除以兼容遗留库);session.service 软删除/listDeleted/restore/purge/purgeExpiredTrash;searchMessages 聚合剔除;IPC listTrash/restore/purge;main.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:updateInstall;preload onUpdateStatus/updateInstall;LogsSettings UpdatePanel(检查/下载安装/进度);electron-builder.yml publish generic | ✅ |
|
|
||||||
| P2-4 @ 文件提及 | workspace.listFiles(2000 上限/噪声目录跳过/深度 8)+ readFileClip(边界/512KB/NUL 拒绝/MEMORY.md 保护);IPC + preload + 类型;ChatInput:@ 触发联想(Fuse 模糊 + 键盘导航)、applyMention 片段替换、发送时 extractMentions(邮箱防护/尾点剥离/≤5 个)→附件管线注入 + 失败 toast | ✅ |
|
|
||||||
| P2-5 MCP 发现 | connectServer try/catch listResources/listPrompts(可选能力降级空数组);getServerContents;IPC mcp:listServerContents;MCPSettings 每 server 展开"资源与提示词";i18n zh/en | ✅ |
|
|
||||||
| P2-6 文档对齐 | 内部 API 标准 HTML:Adapter 清单补 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.0;typecheck/lint/双模式全量三绿(见顶部全局验证) | ✅ |
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
# v0.8.1 迭代实施清单 —「记忆深化 · 观测闭环 · 体验收口 · 配置单一化」
|
|
||||||
|
|
||||||
> 版本基线:v0.8.0(8398600)→ 0.8.1
|
|
||||||
> 本文档是 v0.8.1 的**唯一实施与验收依据**。每项含:根因 / 根治方案 / 涉及文件 / 验收标准。
|
|
||||||
> 硬性契约(用户指令,贯穿全版本):**代码中不存在任何写死的上下文长度或最大输出上限;
|
|
||||||
> 唯一合法来源是设置面板 LLM 配置的「上下文长度」(llm.contextWindow)与「最大输出上限」
|
|
||||||
> (llm.maxTokens),对一切 Provider / 模型生效,不做模型钳制。**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## P0 — 正确性收口
|
|
||||||
|
|
||||||
### P0-1 上下文窗口 / 最大输出上限 全局单一配置化(硬性契约,取代原"按模型真值修正")
|
|
||||||
- **根因**:① 六家 adapter 各自持有 MODEL_INFO 的 contextWindow / maxOutputTokens 写死值,
|
|
||||||
并按其钳制 max_tokens(vision-exp 128K 与配置 1M 冲突时压缩阈值失真,先 413 再压缩);
|
|
||||||
② base-adapter / openai-compatible-base / engine / agent-store / 引导页均有写死兜底
|
|
||||||
(128K / 4096 / 1M / 63488 / 2048);③ 分 Provider contextWindow 配置键与 ollama.numCtx
|
|
||||||
造成多源语义。
|
|
||||||
- **根治方案**:
|
|
||||||
1. CONFIG_DEFAULTS 新增 `llm.contextWindow`(种子默认 131072)为唯一上下文窗口配置;
|
|
||||||
`llm.maxTokens`(63488)为唯一输出上限配置;删除 deepseek/agnes/mimo/openai/
|
|
||||||
anthropic.contextWindow 与 ollama.numCtx 五个种子键。
|
|
||||||
2. 迁移 11(SCHEMA_VERSION 3→4):episodic/semantic_memories 增 embedding BLOB 列(P1-1);
|
|
||||||
迁移 12:DELETE 存量库中已废除的六个配置键。
|
|
||||||
3. 全部 adapter 的 MODEL_INFO 删除窗口/上限数值字段;deepseek/agnes/mimo/openai 的
|
|
||||||
max_tokens / max_completion_tokens / num_predict 原样透传 `params.maxTokens`(未配置
|
|
||||||
不下发);anthropic 删除模型钳制,仅保留 thinking 协议下限 2048(协议不变量,
|
|
||||||
非输出上限);ollama 删除 num_ctx 探测缓存与 4096 默认。
|
|
||||||
4. getContextWindow 契约单一化:config(设置面板值)> 0 返回之,否则返回 0
|
|
||||||
(引擎 syncContextWindow 仅采纳 >0;压缩预算 effectiveContextWindow<=0 时跳过压缩)。
|
|
||||||
5. engine DEFAULT_CONFIG 删除 contextWindow/maxTokens;AgentLoopConfig 二字段改可选;
|
|
||||||
SubAgent(orchestrator)不再携带 63488/128_000 兜底。
|
|
||||||
6. main.ts / shared.ts(LLM_CONFIG_KEYS、applyEngineConfigKey):
|
|
||||||
`llm.contextWindow` 全局键驱动 adapter 重建 + 引擎 contextWindow 热更新,
|
|
||||||
Ollama 场景同步 contextLength(num_ctx);分 Provider 键进入兼容分支仅 WARN。
|
|
||||||
7. LLMSettings 重写:全局唯一「上下文长度」「最大输出上限」两个输入项;
|
|
||||||
删除 supportsOutputLimitConfig 门控、模型元信息钳制提示与 numCtx 分字段;
|
|
||||||
OnboardingWizard 删除 DEFAULT_CTX 写死表,写 `llm.contextWindow`。
|
|
||||||
8. agent-store 删除 4096/1M 常量;setProvider 读 `llm.contextWindow`;新增
|
|
||||||
setContextWindow 热更新(config:changed 联动)。
|
|
||||||
- **涉及文件**:database.service / main / ipc/shared / ipc/agent(无)/ engine / agent-loop
|
|
||||||
types / 六家 adapter / base-adapter / openai-compatible-base / agent-store / useAgentStream /
|
|
||||||
LLMSettings / OnboardingWizard / model-capabilities / 相关测试
|
|
||||||
- **验收**:typecheck 三绿;max-tokens-clamp 契约测试重写为"原样透传 + 未配置不下发 +
|
|
||||||
anthropic thinking 协议下限";provider-request-shapes 钳制矩阵全部改为透传矩阵;
|
|
||||||
E2E 冒烟断言请求体 max_tokens === 设置值(2048)。
|
|
||||||
|
|
||||||
### P0-2 记忆生命周期接线(死账清理)
|
|
||||||
- **根因**:`clearWorkingMemory` 零调用方(working_memories 随会话永久残留);
|
|
||||||
`episodic_memories.expires_at` 无写入方(cleanupExpired 空转);semantic
|
|
||||||
`access_count` 只读不写(LRU 淘汰死语义)。
|
|
||||||
- **根治方案**:会话终态(sessions:delete / purge / clearMessages、data:clearSessions、
|
|
||||||
SubAgent 终态 finishSubTrace 与 abort 路径)调用 `memoryManager.clearWorkingMemory`;
|
|
||||||
MemoryTriggerHook 写 episodic 时携带 90 天 TTL;`search()` 命中后回写 access_count。
|
|
||||||
- **涉及文件**:memory/manager、hooks/post-tool、ipc/sessions、ipc/agent、ipc/data
|
|
||||||
- **验收**:memory-manager 测试新增 expiresAt 写入 / access_count 递增用例(Electron ABI)。
|
|
||||||
|
|
||||||
### P0-3 回放缓冲终态清理
|
|
||||||
- **根因**:v0.8.0 引入的 replayBuffers 为 ipc/agent.ts 内部 Map,会话删除/彻底删除
|
|
||||||
无联动清理,已删会话最多 4MB/会话滞留(仅 LRU 50 兜底)。
|
|
||||||
- **根治方案**:抽为 `electron/ipc/replay-buffer.ts` 独立模块(append/reset/mark/clear/get);
|
|
||||||
sessions:delete / purge 调用 clearReplayBuffer。
|
|
||||||
- **涉及文件**:ipc/replay-buffer(新)、ipc/agent、ipc/sessions
|
|
||||||
- **验收**:replay-buffer.test 六用例(有界、truncated、runId、TERMINATED 保留、INIT 重置、
|
|
||||||
终态清除)全绿。
|
|
||||||
|
|
||||||
### P0-4 i18n 收口第二期
|
|
||||||
- **根因**:主进程 toast/系统通知(压缩、死循环、故障转移、固化、sendMessage 错误路径、
|
|
||||||
更新/完成通知)与渲染层 14 处 toast 硬编码中文。
|
|
||||||
- **根治方案**:新增 `electron/utils/main-locale.ts`(ui.locale 驱动、mt(key,params) 取词、
|
|
||||||
zh/en 双表、启动注入 + applyConfigSideEffects 热切换);ipc/agent.ts、main.ts、ipc/shared.ts
|
|
||||||
全部文案出层;渲染层 agent-store / App / useKeyboardShortcuts / UserMessage / useAgentStream
|
|
||||||
(Provider 切换、输出验证)出层并入 i18n-strings(zh/en)。
|
|
||||||
- **涉及文件**:utils/main-locale(新)、ipc/agent、ipc/shared、main、agent-store、App、
|
|
||||||
useKeyboardShortcuts、UserMessage、useAgentStream、i18n-strings
|
|
||||||
- **验收**:`grep` 断言渲染层 toast 调用零硬编码中文;typecheck/lint 绿。
|
|
||||||
|
|
||||||
## P1 — 能力演进
|
|
||||||
|
|
||||||
### P1-1 本地向量混合检索(激活 embed 死代码)
|
|
||||||
- **根因**:OllamaAdapter.embed() 全项目零调用;TF-IDF bigram 对同义改写零召回。
|
|
||||||
- **根治方案**:`memory/embedder.ts` 契约 + main.ts 装配(仅 Ollama Provider 且用户配置
|
|
||||||
`memory.embeddingModel` 时启用,adapter 闭包动态读取);迁移 11 embedding BLOB 列;
|
|
||||||
store 写入后异步回填向量(in-flight 去重);search 升级 async:混合评分 =
|
|
||||||
0.6×向量余弦 + 0.4×TF-IDF(各自叠加时间衰减与重要度;单路缺失回退单路);
|
|
||||||
**存量记忆惰性回填**:检索候选中 embedding 为 NULL 的行排队补算(本轮仍走 TF-IDF,
|
|
||||||
后续查询命中向量路径,无独立迁移任务、嵌入器不可用零开销);AgentSettings 增
|
|
||||||
「向量嵌入模型」配置(空 = 关闭)。
|
|
||||||
- **涉及文件**:memory/embedder(新)、memory/manager、main、ipc/memory、AgentSettings、
|
|
||||||
i18n-strings、database.service(迁移 11)
|
|
||||||
- **验收**:memory 套件 86 用例绿(含 5 个新增:TTL 写入、access_count、同义改写混合命中、
|
|
||||||
embedder 抛错/返回 null 回退、异步回填 BLOB)。
|
|
||||||
|
|
||||||
### P1-2 MEMORY.md 维护闭环
|
|
||||||
- **根因**:固化 append-only;固化 prompt 全文截 3000 字符 → 尾部条目对 LLM 不可见,
|
|
||||||
去重失效重复写入;无任何回收路径,MEMORY.md 无限膨胀。
|
|
||||||
- **根治方案**:① digest 共享化 —— `parseMemoryEntries` / `buildMemoryEntriesDigest`
|
|
||||||
(纯条目行、8000 字符预算)同时用于固化去重与维护分析;② `memory/maintainer.ts`
|
|
||||||
两阶段维护:analyze(LLM 产出 delete/update 建议,条目精确匹配校验防幻觉)→
|
|
||||||
apply(用户勾选确认后:重写 MEMORY.md —— workspaceService.rewriteMemory 原子写、
|
|
||||||
同步 semantic_memories、审计留痕);③ IPC `memory:analyzeMaintenance` /
|
|
||||||
`memory:applyMaintenance`(结构校验 + 审计);④ MemoryViewer「整理记忆」入口 +
|
|
||||||
建议勾选弹框;⑤ MEMORY.md > 50KB 固化后 WARN 提示。
|
|
||||||
- **涉及文件**:memory/maintainer(新)、memory/consolidator、services/workspace、ipc/memory、
|
|
||||||
ipc/context、main、preload、global.d.ts、MemoryViewer、ipc/agent(超限告警)、i18n-strings
|
|
||||||
- **验收**:maintainer.test 五用例(解析、digest 无盲区、精确匹配防幻觉、update 双轨同步、
|
|
||||||
动作数上限)+ consolidator 套件回归全绿。
|
|
||||||
|
|
||||||
### P1-3 上下文 / 成本可观测闭环
|
|
||||||
- **根因**:adapter 采集的 cacheHitTokens/cacheMissTokens 在引擎 USAGE 映射与前端
|
|
||||||
TokenUsage 类型被丢弃;ChatInput 无上下文占用指示(UI/UX 文档预留)。
|
|
||||||
- **根治方案**:engine TokenUsage 增缓存字段并随 USAGE/accumulate 透传;agent-store
|
|
||||||
TokenUsage 扩展 + useAgentStream 累加(Provider 不上报保持 undefined);TokenUsage
|
|
||||||
面板新增「Prompt 缓存命中」行与「估算成本」行(单价 llm.priceInput/llm.priceOutput
|
|
||||||
为设置面板可选配置,未配置隐藏 —— 成本无任何写死价格);ChatInput 顶部上下文占用
|
|
||||||
指示条(lastInputTokens / llm.contextWindow,60%/80% 变色)。
|
|
||||||
- **涉及文件**:agent-loop/types、engine、agent-store、useAgentStream、TokenUsage、ChatInput、
|
|
||||||
LLMSettings(单价输入)、i18n-strings
|
|
||||||
- **验收**:typecheck/web 绿;E2E 冒烟在真实渲染界面运行(contextWindow/maxTokens 种子值
|
|
||||||
驱动)。
|
|
||||||
|
|
||||||
### P1-4 MCP Prompts / Resources 可用化
|
|
||||||
- **根因**:v0.8.0 仅"发现与列表",prompts/resources 在对话中不可用。
|
|
||||||
- **根治方案**:mcp-manager 增 getPrompt(prompts/get,单 ContentBlock 文本提取)/
|
|
||||||
readResource(resources/read,二进制 blob 显式拒绝);IPC `mcp:getPrompt` /
|
|
||||||
`mcp:readResource`(512KB 截断);ChatInput:斜杠菜单动态合并 MCP prompts
|
|
||||||
(`/mcp:{server}:{prompt}` → getPrompt 填充输入框);@ 提及合并 MCP resources
|
|
||||||
(`@mcp:{server}:{uri}` → readResource 注入附件管线,token 集合扩展 ':');
|
|
||||||
server 不支持时菜单自然不出现。
|
|
||||||
- **涉及文件**:mcp-manager、ipc/mcp、preload、global.d.ts、ChatInput、i18n-strings、
|
|
||||||
mcp-contents.test(新)
|
|
||||||
- **验收**:mcp-contents.test 五用例(getPrompt 展平 / 未连接抛错 / text 返回 / blob 拒绝 /
|
|
||||||
空 contents null)+ ChatInput typecheck。
|
|
||||||
|
|
||||||
## P2 — 体验补全
|
|
||||||
|
|
||||||
### P2-1 工具自定义策略(UI/UX 文档预留项落地)
|
|
||||||
- **根治方案**:permissions.ts 增 `parseToolPolicy`(JSON 配置 → 正则编译,非法正则跳过、
|
|
||||||
fail-closed)与 `setPolicyOverride/getPolicyOverride`(覆盖层优先于默认策略,字段级合并
|
|
||||||
保留默认安全项);存储键 `tools.{name}.policy`;main.ts 启动冷加载 + shared.ts
|
|
||||||
config:set 热加载(IPCContext 增 policyEngine);ToolsSettings 每工具「策略」编辑器
|
|
||||||
(拒绝/允许正则、频率上限、强制确认,inline 非法正则提示)。
|
|
||||||
- **涉及文件**:sandbox/permissions、ipc/context、ipc/shared、main、ToolsSettings、i18n-strings
|
|
||||||
- **验收**:typecheck/lint 绿;覆盖层优先级由 resolvePolicy 单一解析点保证。
|
|
||||||
|
|
||||||
### P2-2 连续同类工具批量确认聚合(UI/UX 文档预留项落地)
|
|
||||||
- **根治方案**:ConfirmationHook 聚合窗口(800ms)—— 同 (session, tool) 并行请求达
|
|
||||||
3 条即 flush 为单条 `tool:confirmationRequestBatch` 广播(低于阈值逐条广播,原行为);
|
|
||||||
clearPending 同步丢弃未广播缓冲;preload/global.d.ts 增批量监听;ConfirmationDialog
|
|
||||||
消费批量事件(倒计时取最早 expiresAt)。
|
|
||||||
- **涉及文件**:confirmation-hook、preload、global.d.ts、ConfirmationDialog、
|
|
||||||
confirmation-hook.test(广播测试改 fake timers + 新增 2 用例)
|
|
||||||
- **验收**:hooks 套件 48 用例绿(≥3 聚合单事件、<2 逐条)。
|
|
||||||
|
|
||||||
### P2-3 会话消息游标分页加载
|
|
||||||
- **根因**:sessions:getMessages 全量加载无上限,超长会话切换 IPC 载荷大。
|
|
||||||
- **根治方案**:session.service 增游标语义(无 limit 全量兼容;limit 无游标 = 尾部窗口;
|
|
||||||
limit+beforeRowid = 游标前 N 条,统一升序);IPC 参数校验;preload/global.d.ts 类型;
|
|
||||||
agent-store 首屏尾部窗口(200 条)+ loadOlderMessages(防重入 + 完整性判定 +
|
|
||||||
竞态保护)+ ChatMessage.rowId;MessageList startReached 触发向上加载。
|
|
||||||
- **涉及文件**:session.service、ipc/sessions、preload、global.d.ts、agent-store、
|
|
||||||
MessageList、session-pagination.test(新)
|
|
||||||
- **验收**:pagination.test 四用例(全量兼容 / 尾部窗口 / 游标翻页 / 开头完整性)绿。
|
|
||||||
|
|
||||||
### P2-4 开机自启
|
|
||||||
- **根治方案**:IPC `app:setLoginItem` / `app:getLoginItem`(app.setLoginItemSettings +
|
|
||||||
读回真实生效状态,Linux 不可用平台 fail-safe);AppearanceSettings 增开关
|
|
||||||
(乐观更新 + 读回校正 + 不支持平台提示);preload / global.d.ts 接线。
|
|
||||||
- **涉及文件**:ipc/app、preload、global.d.ts、AppearanceSettings、i18n-strings
|
|
||||||
- **验收**:typecheck 绿;读回语义保证 UI 与平台真实状态一致。
|
|
||||||
|
|
||||||
### P2-5 E2E 冒烟(Playwright + Electron)
|
|
||||||
- **根治方案**:`e2e/mock-llm.ts`(本地 OpenAI 兼容 SSE Provider,随机端口,零外联);
|
|
||||||
`e2e/metona.spec.ts`(隔离 userData + 种子确定性合法 LLM 配置 → 发消息 → 断言流式
|
|
||||||
回复渲染 + 请求体携带设置 maxTokens);playwright.config;main.ts 增
|
|
||||||
`METONA_USER_DATA_DIR` / `METONA_E2E_SEED_CONFIG` 引导钩子(显式 env 才生效);
|
|
||||||
npm script `test:e2e`(build + playwright)。
|
|
||||||
**附带根治三项环境级缺陷**:① 权限白名单在 app ready 前注册抛异常静默失效
|
|
||||||
(延迟到 ready 后,防线真正生效);② 代理 dispatcher 不放行回环目标(Loopback
|
|
||||||
Bypass 组合 dispatcher,本地 Ollama/SearXNG/E2E 全部修复);③ safeStorage 加密
|
|
||||||
回读失败无降级(roundtrip probe,失败会话降级明文存储)。
|
|
||||||
- **涉及文件**:e2e/*(新)、playwright.config(新)、main、utils/network-proxy、
|
|
||||||
utils/secure-config、package.json
|
|
||||||
- **验收**:`npx playwright test` 2/2 绿(本机含代理环境实测通过)。
|
|
||||||
|
|
||||||
## P3 — 收尾
|
|
||||||
|
|
||||||
- package.json → 0.8.1;README 徽章 / 亮点表 / 配置说明 / 测试命令对齐;
|
|
||||||
- IR 标准文档补 usage.cacheTokens 透传与"输出上限无模型钳制"语义;
|
|
||||||
- 全量 typecheck + lint + `npm test`(系统 Node)+ `npm run test:electron`(全量)。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Review 回归修复(2026-09-08 第二轮)
|
|
||||||
|
|
||||||
完整回归 review(配对审计 + 高风险 diff 逐行复核)发现并修复:
|
|
||||||
|
|
||||||
| # | 类型 | 内容 |
|
|
||||||
|---|---|---|
|
|
||||||
| R1 | 真 bug | **MCP Prompt 斜杠命令大小写失配** —— cmd 被整体 toLowerCase,大写 server 名与 MCPManager 原始名 Map key 失配("not connected");改为对 mcpPrompts 清单大小写不敏感匹配反查原始名 |
|
|
||||||
| R2 | 状态一致性 | **分页状态复位缺失** —— agent-store 的 clearMessages / resetSessionState 未复位 messagesComplete/loadingOlder;已补 |
|
|
||||||
| F1 | 观察项 O1 | **LLMSettings 清空输入静默回写种子默认值**(63488/131072)+ shared.ts `Number(null)=0` 会把引擎预算清零 —— 语义改为"清空 = 写入 null(未配置)":引擎跳过压缩预算 / 输出上限参数不下发(由服务端默认值决定),helper 文案同步 |
|
|
||||||
| F2 | 观察项 O2 | **维护弹框空分区提示** —— analyze proposal 增加 sectionEntryCounts,弹框计算"应用后变空的分区"并向用户提示(分区头保留) |
|
|
||||||
| F3 | 告警位置 | **MEMORY.md >50KB 告警移出固化分支** —— 此前仅固化触发时检查,跳过固化的大文件永不告警;改为每次成功 run 后检查 + 每会话一次去重 |
|
|
||||||
| F4 | 历史 schema | **working_memories 缺 sessions 外键**(v0.2.0 建表起缺失级联)—— 迁移 13(SCHEMA_VERSION 5):孤儿行清理 + 带 FK(CASCADE) 重建;createTables 新库口径对齐 |
|
|
||||||
| F5 | 历史残留 | **全局配置层废键未清理** —— GLOBAL_KEY_PREFIXES 移除五个 provider 前缀与 ollama.(其下唯一键已废除);initialize 清除全局 JSON 中的 DEPRECATED_CONFIG_KEYS,与工作空间迁移 12 对齐 |
|
|
||||||
|
|
||||||
**验证**:typecheck 0 错误 / lint 0 问题 / test:electron **2478/2478**(净增 sectionEntryCounts 用例)/ E2E 2/2。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 验证记录(实施完成后回填)
|
|
||||||
|
|
||||||
> 全局验证(2026-09-07):
|
|
||||||
> - `npm run typecheck` 0 错误;`npm run lint` 0 问题
|
|
||||||
> - `npx playwright test` **2/2 通过**(E2E 冒烟)
|
|
||||||
> - `npm test`(系统 Node):2167 通过 / 310 按 ABI 设计跳过
|
|
||||||
> - `npm run test:electron`(Electron ABI 全量):**2478/2478 通过,0 跳过**
|
|
||||||
> (基线 2445 → 0.8.1 净增 33 个用例:向量混合检索 / 维护闭环 / 回放缓冲 /
|
|
||||||
> 分页 / 批量确认聚合 / MCP contents / secure-config probe / sectionEntryCounts)
|
|
||||||
> - sandbox 符号链接用例曾在本机暴露悬空链接绕过 realpath 的真实缺陷(P0 级),
|
|
||||||
> 已随 0.8.1 根治并通过
|
|
||||||
|
|
||||||
| 项 | 验证方式 | 结果 |
|
|
||||||
|---|---|---|
|
|
||||||
| P0-1 配置单一化 | max-tokens-clamp 透传契约 12 用例;provider-request-shapes 透传矩阵 19 处重写;anthropic/openai/ollama getContextWindow 0 回退契约;迁移 12 清理键;E2E 断言 max_tokens=2048 透传;附带根治:权限白名单 ready 时序 / 代理回环放行 / safeStorage roundtrip 降级 / 悬空 symlink 白名单逃逸(sandbox lstat) | ✅ |
|
|
||||||
| P0-2 记忆生命周期 | memory-manager 新增 expiresAt / access_count 用例;sessions/data/agent 终态接线(ipc 测试 stub 同步) | ✅ |
|
|
||||||
| P0-3 回放缓冲 | replay-buffer.test 六用例 + sessions:delete/purge 联动 | ✅ |
|
|
||||||
| P0-4 i18n 收口 | main-locale zh/en 双表 + 热切换;渲染层 17 处出层;grep 断言零残留 | ✅ |
|
|
||||||
| P1-1 向量混合检索 | memory 套件 86 用例(含同义改写命中 / 回退 / 惰性回填 / BLOB 回填) | ✅ |
|
|
||||||
| P1-2 MEMORY.md 维护 | maintainer.test 五用例 + 固化 digest 共享 + MemoryViewer 弹框 + 审计 | ✅ |
|
|
||||||
| P1-3 可观测闭环 | engine/agent-store cache 字段透传 + TokenUsage 命中率/成本行 + ChatInput 指示条 | ✅ |
|
|
||||||
| P1-4 MCP 可用化 | mcp-contents.test 五用例 + ChatInput 斜杠/@mcp 接线 | ✅ |
|
|
||||||
| P2-1 工具自定义策略 | parseToolPolicy fail-closed 解析 + setPolicyOverride 优先级 + ToolsSettings 编辑器 | ✅ |
|
|
||||||
| P2-2 批量确认聚合 | confirmation-hook.test +2(≥3 聚合 / <3 逐条),既有广播测试 fake timers 适配 | ✅ |
|
|
||||||
| P2-3 游标分页 | session-pagination.test 四用例 + store/MessageList startReached 接线 | ✅ |
|
|
||||||
| P2-4 开机自启 | setLoginItem/getLoginItem + 读回语义 + AppearanceSettings 开关 | ✅ |
|
|
||||||
| P2-5 E2E 冒烟 | playwright 2/2;附带根治权限加固时序 / 回环代理放行 / safeStorage 降级 | ✅ |
|
|
||||||
| 收尾 | package.json 0.8.1;README / IR 标准 / 本清单入库;全量三绿 | ✅ |
|
|
||||||
Reference in New Issue
Block a user