v1.4.1
[1.4.1] - 2026-08-05
Changed
- 分支树面板改造成双模式“分支历史”:默认“分支导航”会折叠连续线性消息,只保留根节点、分支点、候选入口、命名/删除节点与当前尾部;“完整消息”显示所有节点,但普通父子消息沿用同一纵向轨道,只有真正的兄弟候选才横向展开,不再随对话长度形成一路向右的阶梯。面板同时增加节点计数、角色标签、精简时间、悬停操作按钮和窄窗口适配,保留候选切换、工作区恢复确认、软删除、恢复与重命名能力,三语文案同步。
- 子代理完整 transcript 从 conversation
.meta.json迁移到conversations/<conversationId>/subagents/<runId>.json独立原子文件;元数据仅保留状态、计数、修订号和transcriptRef,不再重复内嵌contents与lastSentHistory(包括图片 Base64)。旧内嵌记录首次加载时自动迁移并清除大字段,Monitor 历史与续跑 provider 前缀保持完整。
Added
- 树状分支重 roll 前端主流程接线(此前仅后端完成):消息「重试」与「回档并重试」从破坏性删除(deleteMessage + retryStream)切换为
chat.rerollStream(旧回答保留进分支图 sidecar,新候选生成后可经 BranchSwitcherBar 的「‹ 2/2 ›」切换回);reroll 流结束(complete/error/cancelled)后自动刷新分支图;重试确认框三语文案同步为「保留当前回答、生成新版本」语义;chat.rerollStream加入无超时请求白名单与 VSCodeRequest 类型联合 - 编辑用户消息前端主流程接入
chat.editBranchStream(此前仅后端完成):消息「编辑」与「回档并编辑」从破坏性editAndRetryStream(覆盖原消息)切换为分支编辑——后端创建编辑候选(新 user 节点)并截断主历史,原消息及其子树保留进分支图 sidecar(决策 7:旧分支保留,失败可切回);本地窗口改写目标消息 + 截断 + 流式占位,置位分支图刷新标记(流结束后 BranchSwitcherBar 显示候选切换器);流启动失败时重载最后一页 + 检查点恢复前后端一致;chat.editBranchStream加入无超时请求白名单与 VSCodeRequest 类型联合;附件仅更新本地窗口(编辑分支接口无附件字段) - 子代理设置页工具白名单/黑名单列表工具名称与悬停描述接入三语 i18n:新增公共模块
frontend/src/utils/toolLocalization.ts(工具显示名/描述本地化,缺失时机械转换/回退原文),工具设置页与子代理设置页共用同一套toolDisplayNames/toolDescriptions条目,MCP 外部工具自动回退英文原名 - 删除消息同步软删分支图子树(后端):
deleteToMessage(删除到某条消息)与单条删除在硬删除主历史后同步更新分支图——被删消息对应节点及其后续整棵子树(含非活跃候选)标记为已删除(保留可恢复语义、不物理清理 sidecar,过期清理仍走保留期机制),活跃尾自动回退到最后保留消息对应节点,指向被删节点的活跃指针一并清空;无分支图(线性对话)或无分支服务注册时保持原有删除行为不变,图同步失败仅告警不阻断硬删除(主历史为唯一真源) - 编辑用户消息新增「保持当前分支」模式(
chat.editBranchStream请求新增mode字段,默认'branch'行为不变):mode='keep'为真·原地保存——只改写活跃路径上目标用户消息的文本,后续消息、检查点与分支全部保留,不截断、不软删、不创建候选、不重新生成;后端ensureBranchGraph建图(无图时建线性基线)后updateMessage改写主历史 +updateActiveNodeParts同步图节点内容,流直接完成(complete 仅通知前端复位状态);前端编辑对话框新增「原地保存(保持当前分支)」按钮(三语文案),编辑链路(EditDialog → MessageItem → MessageList → App → editAndRetry → webview)透传mode,分支流错误重放上下文同步携带mode;editAndRetry/restoreAndEdit本地同样只改目标消息(不截断窗口、不创建占位),发送时本地 user 消息补近似parentId(首条为 null)供根节点判断
Fixed
- 修复流停止等待竞态:
waitForIdle()现在同时等待当前控制器和已退休旧流的 finally;cancel()不再让既有等待者永久挂起,也不会在旧流仍处于工具结算窗口时假报空闲;cancelAll()在视图/扩展销毁时统一释放全部退休链。 - 修复删除会话与运行任务/元数据写入竞态:删除前先中止并等待主流退出,退出该会话全部前台与后台子代理,等待 executor 注销及 transcript/索引持久化排空,再删除检查点和会话;会话物理删除同时进入元数据共享写链,进行中的巨大
.meta.json写不能在删除后通过晚到 rename 复活幽灵文件。 - 修复「原地保存(保持当前分支)」误删整个分支:keep 模式原实现为「改写 + 截断其后内容 + 软删图子树 + 重新生成」,用户点击后目标消息之后的全部消息与分支候选被移除。现改为真·原地保存语义(只改目标消息文本,后续消息 / 检查点 / 分支全部保留,不重新生成),后端去掉
deleteCheckpointsFromIndex/deleteMessagesInRange/syncGraphAfterHistoryDelete与工具循环(keep 模式流直接完成),前端不截断窗口、不创建占位、不置分支图刷新标记;错误条重放(replayBranchStreamAfterError)对 keep 上下文同样不截断、不创建占位;根节点编辑降级 keep 时走同一语义 - 修复编辑第一条消息(根节点)报
INVALID_BRANCH_RELATION: cannot edit the root node:BranchGraph 为单根模型,根节点没有父节点可挂编辑候选(TREE-03 遗留「根节点编辑暂拒」)。现在编辑根节点时前端自动降级为「原地保存」语义(keep 模式——只改写根消息,后续内容保留,与编辑对话框「保持当前分支」按钮一致):resolveEditTargetNode新增mode参数、keep 模式放行根节点(图模式与线性模式都支持),editAndRetry/restoreAndEdit检测目标消息parentId == null时自动以mode='keep'发起chat.editBranchStream(错误条重放上下文同步携带 keep 模式);contentToMessage/contentToMessageEnhanced透传content.parentId(Message 类型新增parentId字段),前端据此识别根节点 - 修复编辑用户消息报
NODE_NOT_FOUND: node not found: <id>(新对话首条消息即必现):前端发送用户消息时窗口消息 id 用generateId()生成,但chatStream请求只传文本、不传 id,后端落库时由ensureNodeId另行生成randomUUID()——窗口 id 与主历史/分支图 Content.id 永远不一致,编辑/重试按 id 定位必失败(assistant 消息经流式contentToPersistedMessage有 id 对齐机制,user 消息没有)。现在chatStream请求新增messageId字段(ChatRequestData.messageId),前端sendMessage携带窗口 user 消息 id,后端addMessage原样落库(省略时仍由后端生成,兼容旧客户端);工具确认批注同款修复(annotationMessageId经toolConfirmation→handleToolConfirmation→addContent透传)。已打开会话中的历史遗留不一致消息,重新加载历史(切走再切回)后窗口 id 与后端对齐 - 修复候选切换器(BranchSwitcherBar)挂载位置:切换器改为跟随「当前活跃的候选消息」(用户重 roll / 编辑过的消息本身),而非候选组的父节点——此前
user:1 → ai:2 → ai:3重试 3 后切换器显示在 2 上(候选组挂在父节点 2 下),现在显示在 3'(3 的位置)上;新增buildCandidateGroupForNode(按消息节点推导所属候选组,仅活跃成员返回),BranchSwitcherBarprop 从parentNodeId改为nodeId - 上下文裁剪 fallback 切点回合内稳定(保留 provider 前缀缓存):
getHistoryWithGranularFallback支持stableStartIndex——自动总结失败后同一真实用户回合的多次工具迭代(含工具确认后的续跑)复用第一次确定的裁剪起点,工具结果增长不再每轮把absoluteStartIndex向后推(此前每轮 retainedHistory 开头漂移,缓存只能命中 history 之前的固定系统/工具段);仅当完整性校验失败或估算超过硬上限(maxContextTokens 的 95%)才重新规划;新回合/总结成功后自动清点重新评估,clearTrimState同步清理 - 上下文总结请求不再携带图片/文件载荷:
cleanMessagesForSummarize把用户消息中的inlineData/fileData替换为[Image: …]/[File: …]文本占位符(总结模型无需加载图片字节,省输入 token,也避免不支持多模态的总结渠道报错) - reroll/编辑分支流前端修复:
_pendingBranchRefreshAfterStream标记改为按会话隔离(记录发起流的会话 ID):会话切换后其他会话的终结 chunk 不再误消费并误刷分支图;切回原会话后由该会话的终结 chunk(或后台缓冲 flush)正确消费retryFromMessagereroll 流启动失败且会话已切换时不再重载原会话历史(避免污染当前会话窗口与检查点,与 editAndRetry 同款身份校验)- 内容型
toolIteration因「需用户确认 / 审批门闸 / 工具被取消」终结流时同样消费分支图刷新标记(此前标记残留,可能被后续无关终结事件消费) restoreAndEdit补 R3-#13 同款按 id 重定位(await 恢复期间数组变化不再错改错删),恢复失败错误写入加会话归属校验restoreAndRetry/restoreAndEdit的chat.rerollStream/chat.editBranchStream载荷补齐promptModeId(与 messageActions 入口一致)- i18n 新增
hasMessage()静默 key 存在性检查;工具本地化(toolLocalization)缺失条目不再触发[i18n] Missing translationconsole.warn 刷屏 - reroll/编辑分支流失败时错误条可重试(方案 B):后端在流式失败时把底层
ChannelError.type(API_ERROR/NETWORK_ERROR/TIMEOUT_ERROR/PARSE_ERROR等)透传到错误 chunk({ code, type?, message }),前端isRetryableError对REROLL_ERROR/EDIT_BRANCH_ERROR改为按底层 type 判定可重试——携带可重试 type 时错误条显示「重试」;无 type(如REROLL_FINISH_SYNC_FAILED等 reroll 特有错误)或 type 不可重试(CONFIG_ERROR/VALIDATION_ERROR/CANCELLED_ERROR)时不显示;ErrorInfo类型补可选type字段,retryAfterError入口守卫改用isRetryableError保持与错误条判定一致 - 主聊天发起的 diff 预览同样跟随主聊天所在列:
vscode.diff的viewColumn参数在部分布局/版本下不生效(仍在当前活动编辑器组打开,焦点在 Monitor 面板时 diff 会落到 Monitor 列),现在主聊天上下文同样下发diffViewColumn(与 Monitor 路由同语义,主聊天在侧边栏时回退主区域第一列),且openDiffView打开后用vscode.moveActiveEditor把 diff tab 校正到目标列(目标列即当前列时移动幂等)
- 修复后台子 Agent 完成回执插入尚未结束的主模型调用,并导致上下文从约 35 万 token 骤降至约 2 万 token(用户实测):
- 根因一是前端收到
completechunk 后会先清除isStreaming/isWaitingForResponse,但后端流此时可能尚未执行StreamRequestHandler.finally;后台任务回流仅依据前端状态判断空闲,随即创建同会话新流,触发StreamAbortManager.create()中止并替换仍在收尾的旧流。现在新增后端chat.awaitConversationIdle与StreamAbortManager.waitForIdle(),回执发送前以运行控制器为生命周期唯一事实来源,等待旧流真正退出;等待期间若切换会话或启动新流则重新判定,不再依靠延时或前端瞬时状态猜测 - 根因二是后台回执经普通
chatStream落盘时被标记为真实 user 消息,裁剪器将其识别为新回合;当前一个工具回合本身约 35 万 token 时,回合完整性约束会迫使裁剪器丢弃整个旧回合,只保留约 2 万 token 的回执回合。现在source: 'background_task'从前端请求贯通到后端历史,回执以isUserInput: false保存;合法裁剪起点、回合识别、当前回合定位、思考范围与 token 累加统一复用isRealUserMessage(),后台回执视为原任务的异步延续,旧历史中缺少isUserInput的真实用户消息仍兼容;source在渠道 formatter 发请求前剥离,历史重载时透传回前端以保持后台任务卡片样式 - 补充后端运行生命周期、回合边界、旧历史兼容和前端来源透传回归测试;根 TypeScript 检查、前端生产构建及目标测试通过
REROLL_ERROR/EDIT_BRANCH_ERROR错误条重试改为重放原分支流(方案 B 一致性收口):此前错误条「重试」会回退到retryStream直接追加生成——流式失败后主历史已有半截候选、请求级失败后旧回答仍留在主历史,追加生成分别造成「半截候选 + 新回答」重复与「旧回答 + 新回答」重复,并让分支图与主历史失配(后续删除/编辑报索引越界)。现在retryFromMessage/editAndRetry/restoreAndRetry/restoreAndEdit在发起分支流时记录原请求快照(目标节点、编辑文本、配置、模型覆盖与 Prompt 模式),流失败时随错误对象保存;错误条重试按错误码重放chat.rerollStream/chat.editBranchStream——流式失败(REROLL_ERROR/EDIT_BRANCH_ERROR)复用失败候选(省略旧节点 ID,由后端按当前活跃路径选择,前端仅回滚半截展示、绝不调用deleteMessage),请求级失败(RETRY_ERROR/EDIT_RETRY_ERROR)按原目标节点重建本地窗口后重放;重放上下文随标签页快照保存/恢复(切标签页后仍可重试),成功/取消/关闭错误时清理;普通流错误(STREAM_ERROR/API_ERROR等)重试行为不变
- 根因一是前端收到
- 修复
agent.sendMessage工具名含点号导致 OpenAI 兼容 API 拒绝请求(400Invalid 'tools[N].function.name': string does not match pattern '^[a-zA-Z0-9_-]+$'):工具更名为agent_send_message(与全局 snake_case 命名一致),并在声明中注册agent.sendMessage为别名,旧对话历史中的调用仍可经 ToolRegistry 别名解析执行 - 子代理设置页工具白名单/黑名单列表不再把工具完整描述直接渲染在名字下方(几十个工具的长英文描述把设置页撑得极大),改为仅显示工具名,描述收敛为鼠标悬停的 title 提示
- 批量代码审查修复:
- 工具系统:validateToolArgs 对非对象参数加守卫(畸形模型输出不再 TypeError 崩溃整个工具批次);insert_code 空内容不再产生多余空行;jsonFormatter 字符串值内出现字面
<<<END_TOOL_CALL>>>标记不再提前截断块(状态机感知字符串开关与转义) - 对话历史:rejectToolCalls 对无 parts 消息加防御;getCustomMetadata 在 meta.json 损坏时降级返回 undefined 而非抛错;删除会话后迟到的 setTitle/updateSummary 不再重建幽灵 meta.json(loadMetadataForWrite 校验历史存在性)
- 文件与搜索:generate_image 参数缺失路径不再残留 running 任务;网络错误不再误报为用户取消;remove_background mask 缩放改 fit:'fill' 消除裁切错位;find_files maxResults 语义修正(+1 探测避免 truncated 误报);search_in_files replace 模式匹配收集加 20000 上限与 truncated 标志、多根工作区 path 解析失败不再静默回退、正则源超 500 字符拒绝(ReDoS 防护)
- 子代理与文档工具:agent_send_message 消息加 16000 字符与 inbox 50 条上限;子代理排队取消路径收敛到 finalizeRun 终态出口;update_progress latestConclusion 归一化;record_progress_milestone 里程碑 id 撞车自动递增重试;create_progress 区分 ENOENT 与其它读错误;create_design 不再静默覆盖已存在设计文档;toggle_skills 未找到技能改 data.warnings 语义
- 记忆与设置:truncateLog 长度读取移入锁内(并发 note 不再被误截断);compress 仅在 treePut 成功时上报 done;wake 缺失摘要提示指向实际缺失块;SkillsManager.refresh 同步清理已删除技能;代理设置不再默认启用;设置文件损坏抛错而非静默归零;savePromptMode 校验 mode.id 非空
- webview:MessageRouter 流式请求 clientId 映射改为流结束时统一清理(Monitor 发起的流出错不再错投主聊天或永久挂起);cancelStream 缺 data 不再泄漏映射条目;diff 预览内容缓存加 50 条上限;getExtensionVersion 收敛到公共模块;UsageHandlers dispose 清空统计缓存;删除 FileHandlers 死代码变量
- 前端:流式终结 chunk 缺 content 时流状态仍无条件复位(不再卡在"等待响应");视频/图片缩略图加载加整体超时与 onerror 兜底;声音去重集合与 todo 状态集合加容量上限;待确认工具拦截路径不再静默丢弃附件;后台会话流式缓冲加 2000 chunk 上限
- 构建与测试:.vscodeignore 补充 .env/.env./.pem/*.key 防密钥入包;jest 全局 testTimeout 20000;移除空 jest.setup 引用;messageRouterNonBlocking 测试改为断言生产常量(不再是无效测试);tsconfig 移除 tsc 直出遗留死配置;npm scripts 收敛;fast-tavern 恢复被删除的 build 模块(buildPrompt/buildPromptFromSillyTavern,npm build 与 pytest 恢复通过)、vectorSearch 类型收敛为同步并修正 Python 端异常静默、Python 正则/世界书浮点语义对齐 TS(NaN/Inf 守卫、不再 int 截断)、递归上限对齐 Math.trunc+clamp
- 工具系统:validateToolArgs 对非对象参数加守卫(畸形模型输出不再 TypeError 崩溃整个工具批次);insert_code 空内容不再产生多余空行;jsonFormatter 字符串值内出现字面
- 第二轮高风险并发/性能修复:
- 流式取消竞态(ToolIterationLoopService + ChatFlowService):取消时先以 3s 有界窗口等早启动工具落定再结算(真实结果不再丢失为 cancelled 占位);主工具循环 gen.next() 与 handleToolConfirmation 两个确认循环补 abort-race + 2s 收尾窗口(不再永久挂起);流式取消分支补 resolveAndPersistPostToolStopState(审批门不再残留误拦截)——配套 5 个时序测试
- 代理模式取消:executeStreamRequest 代理分支循环结束后显式检测 externalSignal.aborted 抛 CANCELLED_ERROR(与原生 fetch 分支对齐,半截流不再被当完整消息落盘)+ 剩余 buffer 取消后不再冲刷——5 个测试
- MCP 连接生命周期:connect 代际号机制(旧连接的晚到 exit/error/catch 不再误删新客户端/覆盖状态);connect 按 serverId 复用 in-flight promise(不再假成功);StdioClient spawn 'error' 立即 reject(不再挂满 30s 超时)+ 流 error 监听 + stderr 64KB 上限;HttpClient 超时覆盖 body 读取与 sendNotification、disconnect abort 进行中请求、SSE 按请求 id 匹配与多行 data: 合并——21 个测试
- 依赖安装与配置导出:DependencyManager 同依赖 in-flight 复用 + 每次安装独立临时目录 + exec maxBuffer 64MB + 失败清理 temp;exportConfig 递归脱敏(customHeaders/customBody/tokenCountApiConfig 不再泄漏密钥)
- 前端依赖矩阵统一:vite ^5→^6.4.3、@vitejs/plugin-vue ^5→^6.0.8、vitest 4 保持,删除 esbuild overrides,npm dedupe 后测试与生产构建统一到 vite 6.4.3 + esbuild 0.25.12(npm ci 可复现)
- 锁与设置:fileWriteLockManager 锁 key 统一绝对规范路径(相对/绝对/../写法不再绕过互斥);registerAllTools 改注册真实工厂(refreshTool 不再静默空操作);SettingsCore.updateSettings 深合并(嵌套部分对象不再抹掉同层配置);VSCodeSettingsStorage 按快照 diff 只写变更键(不再每次全量重写全部配置)
- 并发一致性:runEventBus 持久化队列改按 conversationId 串行(同会话并行子代理不再互相覆盖 transcript);新增 progressWriteLock per-path 写互斥(progress.md 读改写不再丢失并行更新)
- 性能:TranscriptRepository 结构化变更少一次写后全量回读+深拷贝;ToolExecutionService 每工具批次节点反查 2 次→1 次(200 次迭代省数百次全量读);PromptManager 固定文件 TTL+mtime 缓存与 1MB/2MB 大小限制、fileTree 节点预算截断(10000)、gitignore
!否定语义、glob 正则编译缓存;checkpoint 三处 O(n²)→O(n)(unbackedPaths/RetentionService 后继/getIncrementalChain)+ 读取侧 backupDir 越界校验 - 测试稳定性:toolBatchCheckpoint 轮询改为等待 after 最终绑定值(消除 fire-and-forget 绑定窗口的既有 flaky)
- 安全/性能/一致性修复:
- webview 安全:diffContentId 白名单校验(^[A-Za-z0-9_-]+$,5 处 loadGlobalDiff 调用点封堵 ../ 穿越);消息路由 clientId 改为按来源 webview 固定(不再信任消息体字段,杜绝跨面板身份伪造)
- media 工具工作区护栏:5 个图片工具(generate_image/remove_background/crop/resize/rotate)输入输出路径统一走 resolveFileToolPathWithInfo + outside-workspace 审批流,与 file 工具策略对齐(默认 deny 下不再能读写工作区外)
- 路径校验:integrityCheck 段文件名白名单(恶意索引不再越界读盘);BranchGraphRepository(新错误码 INVALID_CONVERSATION_ID)与 DiffStorageManager 会话 ID 白名单
- 请求一致性:ChatFlowService 前置清理(diff 中断 + rejectAllPendingToolCalls)提取为公共方法并接入非流式 handleChat/handleRetry(悬空 functionCall 不再跨回合残留);drainInboxIntoResults 显式校验 mailbox drain 持有者(并发主循环不再窃取消息)
- subagents:executor 超时/父取消直接丢弃 partial response 走终态(半截工具调用不再进 transcript);agentMailbox 线程深度原子递增 + 拒绝时清理(不再只增不删);subagents 工具名列表按 registry/MCP 计数指纹缓存(声明访问不再 O(N) 全量重算)
- 大文件护栏:apply_diff/insert_code/delete_code 同步读取前 stat 5MB 上限;remove_background 文件 50MB + 像素 16MP 双层拦截;apply_diff 精确匹配 10 万候选上限 + LCS DP 行数护栏(病态输入不再撑爆内存)
- 工具护栏:list_files 递归深度 10/条目 5000 + truncated 标志 + 跳过常见大目录;execute_command shell 检测结果缓存(不再每工具创建多次同步 execSync);find_references context 钳制 0~10;goto_definition 括号计数词法感知(字符串/注释/模板不参与)
- memory/prompt/settings:recall 淘汰 O(1) 索引指针(不再反复 shift);updateConfig 配置项边界校验(entryChars 1~319 等);listEntries 流式扫描 + 可选 limit;buildPromptCacheKey 纳入模板指纹(改模板不再 60s 内返回过期提示词);SettingsExporter YAML 双引号转义 + 解析端配套反转义(描述含换行/引号/--- 往返不损坏)
- webview 性能:附件 base64 上限 50MB;searchWorkspaceFiles limit 钳制 200 + glob 元字符剥离 + 子目录并行遍历;上下文预览改异步写;BranchHandlers 图信息单次 DFS(O(n²)→O(n));死代码清理(MessageRouter 两方法、jsonFormatter 三导出、StreamProcessorResult 接口)
- ConversationManager:insertMessage/insertContent 后 repairParentChainAfterInsert(插入点后继 parentId 重链,恢复线性活跃路径不变量);结构性删除后同步 messageCount(列表计数不再漂移);toolAdapter 旧 mcp_ 命名收敛到 codec(mcp__,删除死导出);create_progress 并入 per-path 写互斥
- 前端:sanitizeHtml 补齐(xlink:href/use href/data:/srcdoc/style 剥离、控制字符混淆防护);删除 useChat/useMessages/useConversations 三个死 composable(含从不释放的监听器);sendToExtension 条件 JSON 往返 + 失败回退(纯 JSON 大载荷不再双份序列化);settingsStore Language 补 'ja' + useI18n 接入 ja 语言包(ja 用户 store 级文案不再回退中文)
- fast-tavern:无 id 正则回退 id 改确定性 FNV-1a 哈希(TS/Python 输出一致,可复现);死代码三元收敛;py README 示例命名与代码块修正;根 package.json 新增 benchmark script 与 engines.node(>=20);BranchGraphRepository 原子写 rename 重试(Windows EPERM 偶发 flaky 根治)
- 代码组织与文档收尾:ChannelManager.getFilteredTools 与 ToolDeclarationResolver 合并(删约 235 行重复实现,主会话工具列表获得 denylist/excludeToolNames 能力,"主会话与 SubAgent 共用同一入口"兑现);review 6 个工具路径策略副本收敛到 progress/pathUtils 共享实现;handleEditAndRetry(非流式)补齐前置清理(与其它入口一致);前端三语语言包删除 useChat/useConversations 孤儿键;sanitizeHtml 补 srcset 候选级协议校验;CHANGELOG 历史条目清除全部内部代号(CP-/BR-/MIG-/TREE-/BCP-/EX-/HIS-/CPF-/MED-/审查/复查/决策/PR # 等 165 处)
- 修复刚完成的助手消息点击「重试」时报
NODE_NOT_FOUND:模型回复落盘后,后端现在把真实的稳定消息节点 ID 随流式终结内容回传;前端在toolsExecuting、awaitingConfirmation、toolIteration、complete与取消收尾阶段用该 ID 替换本地流式占位 ID,并同步消息索引与当前流引用。retryFromMessage因此不再把形如1785860200670_2ojp0foff的前端临时 ID 传给分支图,普通回答、工具调用回答及已落盘的取消回答均使用后端节点 ID。 - 修复工具调用后续接回答重生成时报
INVALID_BRANCH_RELATION,并明确 reroll 为“单条助手消息重新生成”:分支服务现在允许被点模型消息的直接父节点是模型节点,新旧回答在该父节点下形成同级候选;主历史与检查点从被点回答自身开始截断,目标之前的模型工具调用和functionResponse完整保留,因此只重新生成选中的回答,不会退回用户消息重跑整轮工具。 - 修复
read_file单文件调用被空批量参数误判为冲突:部分 function-calling 客户端会为未提供的可选数组自动补上files: [],此前与有效path同时出现会报Provide either path or files, not both.;现在空数组在存在path时按“未提供批量参数”处理,同时仍拒绝无path的空批次及path+ 非空files的真正混合调用。 - 修复排队消息「立即发送」仍会掐断前台子 Agent(用户实测):前台 SubAgent 转后台(detach)此前只挂在
StreamAbortManager.create()(新流启动)上,而队列箭头的sendQueuedMessageNow是先显式cancelStream()再发送新消息——旧流 abort 发生在create之前,父取消信号已传播到子 Agent,等新流创建时 detach 已来不及。现在cancelStream支持preserveSubAgents标记:前端「立即发送」路径携带该标记,后端StreamAbortManager.cancelForNewTurn()在 abort 旧流之前先把该会话活跃前台 SubAgent 转为后台(与 create 路径同款 detach 语义),普通「停止」按钮仍走原cancel保持真正终止;新增回归测试(管理器 detach 顺序矩阵、路由标记透传、前端请求顺序)。 - 修复
find_files等未配置存档的单个只读工具偶发显示“对话已存在”:单工具存档判定现在与批量工具一致,未配置存档时不再提前反查会话节点;ConversationManager同时按会话 ID 合并仍在进行的首次创建,消除显式创建与读路径按需创建的并发竞争,创建完成后的真正重复调用仍保持报错。 - 改善主模型与 SubAgent 的停止响应:主聊天点击停止后立即清除前端流式、加载和等待状态,后端关闭 diff、拒绝悬空工具与持久化清理仍按原顺序完成;Monitor 的暂停、继续、退出请求改为非阻塞路由;SubAgent 工具执行增加 500ms 有界中止收尾,即使工具完全不响应
AbortSignal,run 也会进入 cancelled 并释放控制器、并发席位与文件锁,退出成功后 Monitor 立即隐藏控制按钮。 - 降低
apply_diff接受修改后的等待与界面冻结:目标文件 stat/read 改为异步 I/O;完整 diff 内容先写入 16 条/32MiB 双上限的内存缓存,工具结果立即获得预览引用,JSON 持久化转后台并改为紧凑格式,当前会话预览优先读内存、扩展重启后仍可从落盘文件读取;写前/写后工作区存档和 diff 确认流程保持不变。 - 修复前台 SubAgent 返回后主模型输入从约 30 万 token 突然减少约 5 万的真实上下文丢失:上下文管理不再静默按完整用户回合推进持久
trimState,统一改为模型总结优先;总结范围按 token 预算选择,并可在多轮历史的超长工具回合内部以完整functionCall/functionResponse组为边界切分。工具循环同一用户回合持续工作时仍会逐轮检测上限、最多尝试两次自动总结,失败后仅对当前请求执行不持久化的细粒度安全裁剪;旧版 trim 状态首次读取自动失效,升级前被遮蔽的历史可重新参与评估。 - 上下文总结与裁剪现在独立保留历史真实用户输入:被压缩区间中的用户原话和附件名称按时间顺序注入请求,排除 functionResponse、后台回执与旧总结;首次目标和最近补充在异常大输入触发安全预算时仍优先保留,档案不写回 transcript,避免重复膨胀。functionResponse token 估算同步按实际 API 可见字段计算,diff 与 SubAgent UI 元数据不再虚增裁剪预算;同时修复总结消息被起点归一化误跳过的问题。
- 总结设置新增两个可调项:①「自动总结最大尝试次数」——单个真实用户回合内最多尝试 1-5 次(默认 2),尝试耗尽后仍超阈值时本次请求改用不持久化的细粒度安全裁剪;②「总结模型输入占比」——自动总结单次请求输入占总结模型上下文窗口的 5%-95%(默认 50%),超出时自动缩小总结范围、保留最近一轮工具交互。同时清理总结设置页已迁移的过时「自动总结」区块文案,三语同步。
- 修复分支图异步同步未完成时 functionResponse 可在切换重写中丢失:
rewriteHistoryFromBranchGraph的一致性检查此前只验证非 functionResponse 消息已入图,FR 内容(决策 8 并入所属节点 parts)未校验——FR 图同步未完成/失败时切换会静默丢 FR 内容并误清检查点。现在新增findUnsyncedFunctionResponses纯函数(按所属节点做 FR id 子集匹配、防误报),任一 FR 未同步即拒绝切换并返回BRANCH_OPERATION_CONFLICT,主历史保持不变;补 8 个纯函数用例与集成回归(含补同步后重试幂等)。 - MCP 工具调用全链透传外部
AbortSignal:用户取消回合后 MCP 请求立即中止,不再挂到超时。HTTP 客户端外部信号与内部超时 controller 联动(区分中止/超时文案),SSE 读流外部中止时同步取消 reader;Stdio 客户端外部中止清理 pending、超时与进程退出监听并拒绝;ToolExecutionService→McpManager→ 客户端逐层透传。新增 11 个用例,MCP 6 套件 86 测试全绿。 - LSP 生命周期保护抽成共享模块并接入全部 LSP 工具:
get_symbols的打开文档激活语言服务、超时、取消、瞬时失败重试逻辑提取为lspLifecycle.ts(withTimeoutAndAbort/executeLspCommandWithRetry/openDocumentWithGuard),goto_definition与find_references补齐同等保护——provider 挂起不再无限等待,回合取消立即中止;get_symbols保留原导出常量与行为,新增 21 个测试。 - 修复 SubAgent
continueFromRunId续跑丢失 provider 前缀缓存的真正根因:此前的修复只解决了缓存域(user_id 沿用旧 runId),但续跑请求历史取自 Monitor 展示 transcript——首条# SubAgent Invocation卡片从未发送给 provider,与旧 run 的任何一次已缓存请求从第 0 条就不共享前缀,缓存必然全 miss。现在事件总线持久化「最后一次实际发送给 provider 的 history」(lastSentHistory,发送前已完成 agentInbox 剥离、深拷贝、不污染 Monitor 内容与修订号),续跑严格以它为前缀再追加新用户消息;旧记录缺字段时降级过滤 invocation 卡片。新增前缀不变量测试(续跑首请求 = 旧最后请求 + 新消息、不含 Invocation 卡片、持久化恢复后仍成立)。 - 优化 apply_diff 的固定延迟:写前工作区存档不再阻塞工具启动——批内全部为 diff 审阅类工具(apply_diff / write_file / insert_code / delete_code)时,before 存档与工具前置阶段(读文件 + hunk 规划 + 预览渲染)并发启动,预览先行显示;写盘前由 DiffManager 强制等待存档完成并获取目标文件写盘锁(审阅期间持有、diff 终结释放,与入口持锁语义一致,冲突按既有 lockConflict 语义收敛为拒绝),混合批保持同步语义。结构化 hunk 首次精确匹配计划(fast path 产物)缓存到 pending diff,块级拒绝与最终内容重算直接按计划重放、不再重复全文件扫描(起始内容不一致时自动回退重扫);write_file 新建文件预写空文件同样在 checkpoint 就绪后带临时写锁执行。新增并发时序、锁持有/释放与计划重放等价性测试。
- 修复
get_symbols在大型、尚未打开的 TypeScript 文件上偶发1 file(s) failed to get symbols或无限等待:查询前主动打开文档激活语言服务,瞬时未就绪时重试一次,provider 挂起与用户取消均有界退出;聚合错误现在携带具体文件和 tsserver 原因,并补齐 DocumentSymbol 层级、重试与超时回归测试。 - 修复主聊天或 SubAgent Monitor 获得焦点时原生文件 diff 打开到 Monitor 列:所有写工具共用的 diffManager 在打开瞬间定位主聊天列,
vscode.diff后再按实际 tab 校正目标组,主聊天位于侧边栏时回退第一编辑器列。 - 改进批量
read_file工具卡片摘要:不再把后续文件折叠为+N,而是逐行显示真实文件路径、范围与批次状态;SubAgent Monitor 在窄列下允许运行元信息收缩换行,标题与控制区不再横向溢出。 - 修复分支候选下拉框在消息项
contain: layout定位上下文中横纵坐标重复偏移、窄侧栏右侧溢出的问题:候选浮层改为 Teleport 到body,根据视口宽高选择向上或向下展开并约束左右边界,滚动和窗口缩放时持续重算位置。 - 修复
read_file成功执行但工具卡片摘要显示?、展开后丢失文件的问题:单文件调用同时携带规范化生成的空files: []时不再误入批量分支,摘要和内容面板都会回退使用有效path;新增空批量与单文件路径并存的回归测试。 - 放宽同参数重复失败调用护栏:失败次数改为只统计没有其他真实工具调用介入的连续同签名失败,执行诊断、修改文件或其它成功调用后允许原参数重跑测试;真正连续原样失败仍会被短路,默认系统提示同步允许相关状态变化后的重新验证。
- 完善分支候选切换数据源与状态同步:新增
buildCandidateGroupAt按父节点推导候选组(取代原先只取「活跃尾兄弟组」的buildCandidateGroup),BranchSwitcherBar接收parentNodeId,多个分支点可各自计算「‹ N/M ›」候选状态;候选列表使用 fixed 定位浮层防止被消息滚动容器裁剪;DirtyFilesConfirm由MessageList常驻挂载,分支切换遇到未保存文件时确认框不受组件显隐影响。 - 按消息操作栏重新整理分支交互:候选切换器不再作为消息下方独立一列,改为嵌入
MessageActions,与复制 / 重试按钮保持同一行和高度;分支树面板改为论坛评论树式连接线布局,用竖向树干与横向分叉线表达父子关系,活跃路径只高亮导线,只有活跃尾节点显示「当前」,分支点显示未删除候选数量,并补齐中英日三语文案。 - 删除消息范围或单条消息前等待主流真正退出:
deleteMessage/deleteSingleMessage先经abortAndWaitForCompletion(或cancel+waitForIdle)等待流退出再执行删除,避免停止后的迟到写入覆盖删除结果。 - 修复扩展宿主重启后子代理 Monitor 永久显示 running/queued:加载历史 run 记录时把上一宿主遗留的非终态记录纠正为
interrupted(当前进程仍活跃的 run 不受影响,按快照存在性区分)。 - 修复 shell 可用性检测的命令拼接注入面:
checkShellAvailability的 wsl / where / which 检测从cp.exec字符串拼接改为cp.execFileargv 数组传递(customPath 属用户可控配置,不再拼进 shell 命令)。 - Webview CSP 加固:ChatViewProvider 与 SubAgentMonitorPanel 的内联脚本从
'unsafe-inline'改为 nonce 机制(每面板随机 nonce,脚本标签显式携带),不再依赖内联执行豁免。 - 前端正则高亮 ReDoS 护栏:新增
regexGuard(源长度 500 上限 + 危险分组量词检测 + 构造失败回退),history_search 高亮不再因畸形正则阻塞 Webview 渲染线程(与后端搜索护栏同一限制)。 - 修复手动总结请求丢失当前对话模型:
summarizeContext处理器此前只转发conversationId/configId/abortSignal,前端载荷中的modelOverride在 webview 层被丢弃,频道配置model为空时总结请求仍发送空模型名(HTTP 404: No available providers at the moment)。处理器现原样透传modelOverride,与后端SummarizeService的模型透传修复(含独立总结渠道隔离与默认模型回落)闭环;新增处理器层透传回归测试。 - 修复总结请求不携带当前对话实际选中的模型:频道配置
model为空、用户在界面临时选择模型时,手动总结(SummarizeService.handleSummarizeContext读取请求modelOverride)与流式/非流式自动总结(ToolIterationLoopService两处调用透传当前回合modelOverride)此前均向 OpenAI 兼容接口发送空模型名(HTTP 404: No available providers at the moment);使用独立总结渠道时主动隔离主对话模型,未显式指定总结模型则回落到独立渠道自身默认模型,不再错误继承主对话的modelOverride。 - 修复手动总结假成功:上下文管理关闭时发送路径直接从索引 0 读取历史,忽略已存在的手动 summary 边界(总结消息只插入历史而不物理删除旧消息),界面显示总结成功、下一次请求却仍携带全部旧上下文。现
ContextTrimService在策略禁用时也查找并应用最后一条总结边界作为发送历史起点(decision.action =manual_summary_applied)。
Tests
- 新增分支历史纯布局测试,覆盖 20 条线性消息不增加横向轨道、兄弟候选独立轨道、导航模式连续消息折叠及特殊节点保留;更新组件测试覆盖双模式切换与既有管理交互。
- 新增子代理独立 transcript 新格式/旧数据迁移、文件系统原子写与会话级清理、停止前后空闲等待、运行中会话删除生命周期顺序测试。
Changed
- 存档点排除配置的默认类别行布局修正:「N 条规则」计数与「编辑」按钮组合为右侧操作列整体右对齐(计数紧贴按钮左侧),不再因各勾选框标签宽度不同而在行间漂移居中;补上此前缺失的编辑按钮 hover/禁用样式。
- 子代理工具白名单/黑名单改为按分类分组展示:新增公共工具分类模块
frontend/src/utils/toolCategory.ts(工具设置页与子代理设置页共用同一套分类名/图标映射),子代理面板工具列表从「内置工具 / MCP 工具」两个平铺大列表改为按后端 category 分组的分类卡片(文件、搜索、终端、媒体等,MCP 独立成组,缺省分类归入「其他」);每个工具项显示本地化名称 + 等宽工具 ID + 描述,MCP 分类文案接入三语 i18n;工具设置页同步改用公共分类模块(展示行为不变)。 - 分支树面板「完整消息」升级为「完整消息图」(高级模式):由目录树缩进改为轨道式泳道布局——每条消息一行,轨道列数由同时存在的候选分支决定(候选分支走完即释放轨道、后续新候选复用,不再随消息数量无限向右扩展);分叉以水平连接线表达,活跃路径轨道线高亮;默认折叠连续线性段,新增「展开完整消息」开关可查看全部节点;「分支导航」缩略版保持不变,三语文案同步。
- 总结 Token 统计改为「主上下文压缩量」口径:新增
SummaryTokenStats(被替换历史估算 token、新摘要 token、估算节省量、总结前主上下文 token 与总结后估算值),总结消息落库与前端展示均使用新口径(历史 → 摘要 + 节省量),旧记录明确标注为「总结模型请求输入 → 输出」(beforeTokenCount/afterTokenCount标记 deprecated);会话用量指示器在总结后优先使用新估算值,下一次真实主回复后自动恢复实际用量(timestamp 判断新旧)。 - 被裁剪历史的逐字用户输入保险档案上限从 160k 字符降至 64k(约 40k → 16k token),省略标记文本计入预算,避免保险副本吃掉默认上下文保留预算、使总结后上下文几乎不下降。
- 存储路径加固:conversationId / snapshotId / diffId 统一白名单校验(
^[A-Za-z0-9_-]+$,新增assertSafeStorageId/assertSafeDiffId),覆盖 FileSystemStorageAdapter、UsageIndexStore、DiffStorageManager 与 integrityCheck,从根源拒绝..、路径分隔符、盘符与 URI 编码绕过。 - 根项目与前端锁文件同步(npm audit 均为 0 vulnerabilities,根项目声明 engines.node >=20)。
- 子代理续跑(
continueFromRunId)改为「同一条 run 接着跑」:runId 复用旧 run(Monitor 记录唯一、transcript 一条线连续,不再出现第二条不同身份的记录),身份强制沿用旧 run 的 agent(系统提示/工具集不变,本次调用传入的 agentName 被忽略,旧 agent 已被删除时拒绝续跑),provider 前缀缓存命中条件不变(仍以lastSentHistory为请求前缀、conversationId沿用旧 runId);续跑经run_resumed事件标记,前端工具卡 pending 阶段直接沿用continueFromRunId关联 Monitor。
[1.4.0] - 2026-08-04
Changed
- SubAgent Monitor 运行列表改多行布局(用户反馈):run tabs 从单行横向滚动改为
flex-wrap自动换行,tab 在行内伸展铺满(flex: 1 1 170px),子 agent 数量多时不再横向翻找;限制最大高度约 3 行,run 极多时退化为纵向滚动,不占满整个面板 - Diff 应用与展示性能重构:
apply_diff、write_file、search_in_files共用带编辑距离预算的 Myers 行级差分,移除 Webview 主线程上的二维 LCS 表和模板内同一 diff 最多 5 次重复计算;每个文件/块改为响应式缓存一次统计与行结果,超长展开内容通过VirtualDiffLines仅渲染可视窗口,降低大文件和批量替换时的 CPU、内存与 DOM 开销 - 后端 diff 应用增加快速路径:互不重叠且唯一匹配的结构化 hunks 先规划位置并一次拼接文件,保留依赖前序修改、缩进容错和歧义场景的原逐块语义;unified patch 改为整块单次
splice,fallback 先按首行筛选候选再校验完整块,避免逐行修改数组和无条件嵌套扫描 - 存档排除配置编辑器美化:设置页「自定义排除模式」与「每类别模式编辑」从裸 textarea 改为 chips 风格模式列表编辑器(
PatternListEditor)——已有模式以标签卡片展示、悬停逐个删除,输入框回车添加、粘贴多行自动拆条去重,添加/删除即时保存;类别编辑面板补充标题栏与「清空(恢复默认)」按钮并补齐样式;自定义模式标签旁新增规则数量徽标;三语文案同步 - 后台任务回流卡片新增三段式折叠视图(用户反馈):默认折叠为两行省略,可切换「中展开(约 15 行内滚动查看)」与「完全展开」,三个图标按钮置于卡片头部右侧,当前视图高亮;配合后台回执完整内联修复,长报告不再撑满聊天窗口
- 存档路径安全加固:删除、合并、manifest 读取路径统一经共享
isSafeCheckpointDirName校验(/^cp_[a-z0-9_]+$/i),损坏/恶意元数据中的backupDir不再能触发fs.rm(recursive)递归删除存档目录外内容;恢复侧文案与失败摘要统一走t()并补齐modules.checkpoint.restore.*三语条目(checkpointNotFound / manifestMissing / cannotBuildChain / backupDirNotFound / moreFailures / excludedNote / excludedNoteChanged) - 索引删除补祖先闭包:
deleteCheckpointsFromIndexInternal与批量删除对齐,从所有保留节点向前遍历完整基链,编辑+重试导致消息索引回退时不再删掉保留节点的基快照(不再产生永久断链存档);预览返回deleted与deletedIfUnconfirmed两个计数,与默认执行口径严格一致 - 存档配置保存链路加固:
updateCheckpointConfig改为深合并落盘(部分 exclusion/messageCheckpoint 负载不再覆盖已保存的 profilePatterns 等嵌套字段);enabledProfiles值必须为 boolean("false"字符串不再被当真)、工具列表必须为字符串数组、maxCheckpoints仅接受有限整数(保留 -1 无上限哨兵)、enabled必须为 boolean;自定义模式拒绝*/**//**//*全忽略模式(新增 blanket 原因) - 强制排除大小写折叠扩展到 macOS:
.GIT/NODE_MODULES目录片段与扩展存储绝对路径自排除在 win32/darwin 大小写不敏感卷上不再可绕过,normalizeWorkspaceUri同步折叠;符号链接不再静默丢弃,记录为unsupported_file_type排除条目供预览解释 - 用量索引并发丢失更新根治:
FileUsageIndexStore内部实现 per-conversation 写串行队列(appendUsage / appendUsageMessages / write / remove 全部入队,write 内按条目键去重),并行子代理与主会话的读改写不再互相覆盖;usage.json 改 tmp+rename 原子写;统计 listConversations 失败时跳过 prune 保留内存缓存;fs.watchrecursive 增加递归能力探测,不支持时退化为 mtime 快照比对 - 历史存储读一致性加固:
loadSegmentedHistory读后校验Σcount === totalMessages且段齐全(双 rename 提交窗口不再静默返回错位历史),读侧重试改为 2~3 次带退避;getMessagesPaged初始页先只读浅扫描,仅存在悬空 functionCall 才走深拷贝路径(首屏收益找回);钳制改轻量只读 index(不再每条消息 O(段数) 次 stat);删除会话后 append/mutate 短路(流式中删除不再"无 meta 幽灵复活");自愈优先从可读段重建、totalMessages 重算为 Σcount、批量摘要返回 truncated 标志、段缓存增加字节软上限与按会话分桶失效 - 前端设置页与 checkpoint store 加固:
updateConfigField保存失败回滚字段 + 保存串行化;loadConfig失败时禁用表单并展示错误横幅(不再以默认值渲染导致真实配置被覆盖);checkpoint.restore/deleteBatch/previewExclusions超时豁免;失败路径重载 checkpoints;进度轮询瞬时错误不停止 + 陈旧检测 + 新操作重启;展开对话列表响应校验对话身份;批量删除失败保留列表 + 防重入;mergeUnchanged仅保存成功才同步 chatStore - 恢复错误语义修正:恢复结果(失败/部分失败/未备份警告/成功)改用独立分级提示,不再塞入
chatStore.error;错误条「重试」按钮仅在可重试错误码(STREAM_ERROR 等)时显示,恢复失败后点击不再误触发 LLM 重新生成;恢复确认框固化conversationId(预览/确认期间切对话不再恢复错误对话);previewExclusions/getAllConversationsWithCheckpoints加入非阻塞路由;updateCheckpointConfig响应携带归一化配置供前端校正;checkpoint handler 补入参校验与明确错误码 - 快照构建/查询收敛:
runBounded/hashFileStreaming/isExcludedAbsolutePath三处重复实现收敛到共享模块(checkpointConcurrency/fileHashing/checkpointPathUtils,大小写策略统一);排除预览 samples 按路径排序;设置页统计改有界并发 +getMetadataLight;getCheckpoints区分「无记录」与「读取失败」;移除as any类型绕过 - 消息稳定 ID 地基:
Content新增id/parentId,所有写入路径(append 锁内尾读、mutate 插入、functionResponse 补齐)统一经ensureNodeId生成稳定 ID + 线性 parentId;旧历史在首载/迁移入口按「缺 id 自判定」于会话写锁内确定性补 ID(uuidv5 风格,幂等:多次迁移 ID 集合一致)并原子全量重写一次(totalMessages 不变、指纹校验);formatHistoryForAPI白名单确认不下发 id/parentId;前端 parsers 透传content.id为Message.id(跨加载稳定,不再每次生成) - 前端清理:
summarizeContext/cancelSummarizeRequest从 checkpointActions 迁入 messageActions(re-export 保持导出名兼容);设置页存档项新增「排除详情」入口接通checkpoint.getManifest(前端缺口闭合,旧存档提示不可用) - 子 agent 嵌套能力(用户需求):子 agent 现在可派生子子 agent(subagents 工具对子 agent 开放)——嵌套深度上限 2(主=0/子=1/子子=2)超限拒绝,深度由框架注入不可伪造;父 run 结束级联清理派生子 run;子 agent system prompt 追加使用说明(一般不需要,仅代码需独立复查或主模型明确指令时使用);嵌套派发继承父级工具过滤(只读 agent 不能通过嵌套获得写权限)
- 用户消息插入:主会话工具循环/流式中用户发消息不再排队等整轮结束——
chat.sendInterruptMessage投递到主会话 inbox,随最近一次工具调用结果一起注入给模型(10s/条频率限制、4000 字符上限、仅投递不落历史);sendMessage忙时自动改走插入路径 - 树状分支接线:新建
BranchService(分支图读写删接口 getBranchGraph/getBranchGraphMeta/createRerollCandidate/editCandidate/switchBranchCandidate/deleteBranchCandidate、validateActivePathMatchesHistory活跃路径校验、跨对话导出建模 exportedFrom/exportedRefs、importLinearHistory线性导入);BranchHandlers注册 5 个分支 API;ConversationManager暴露runExclusive公共锁包装(锁序文档化)、createBranchConversation记录 sourceNodeId + 图初始化/导出标注、deleteConversation清理 sidecar;switchBranchCandidate明确不重写主历史(边界留给后续分支任务) - 迁移与完整性:新建
BranchMigration(逐版本迁移注册表 + 链式升级 + 升级前深拷贝失败回滚 + 原子落盘 + 损坏拒绝覆盖);新建backend/tools/maintenance/integrityCheck只读完整性检查(历史 Σcount/段齐全/孤儿段、存档 backupDir/manifest/增量链 base+环、分支 validate + 活跃路径与主历史对比,只报告不修复);复查后分支对比降级为 warning(回收条件注释随分支任务落地更新) - 候选切换全链:
ConversationManager.rewriteHistoryFromBranchGraph(会话写锁内从图活跃路径重建主历史:FR 拆分、幂等、分歧索引、用量重建 + trim 失效)+BranchHandlers.switchBranchCandidate全链编排(切图 → 主历史重写 → 锁外检查点清理 → rewritten 标记,失败回滚图状态);复查后补:FR 消息 id 按「节点 id + FR part id 集」复用旧历史 id(防幂等失效与检查点误删)、invalidate 移至 saveHistory 前(metadata 写失败不再分裂图/历史)、切换前锁内历史/图尾部一致性检测(不一致拒绝切换返回 BRANCH_OPERATION_CONFLICT) - 树状分支前端:
branchActions.ts(切换成功后重载历史 → 重建 messageIndexById/toolResponseIndex → 清理错误条/流式残留 → TODO/Build 重置 → 刷新检查点与分支图,失败快照回滚,BRANCH_BUSY 前端双保险);BranchSwitcherBar.vue(‹ 2/3 › 循环切换 + 候选下拉 + 两步确认删除,无分支图自动隐藏);BranchTreePanel.vue(分支树浮层面板:本地 DFS 组装 + 活跃路径高亮 + 软删灰显/恢复 + 行内重命名);标签页快照保存 branchGraph(修复切标签页残留旧图) - 用量含全部分支:方案 A 读取时合并——统计读取时经
readBranchGraph读 branches.json,主历史消息 id 为权威去重键(图活跃路径仅作旧索引无 id 时兜底),非活跃候选 token 以source='branch'并入各维度 +inactiveBranchTokens细分;复查后补:混合态索引去重修正(historyIdsComplete 判定)、候选节点携带 usageMetadataPartial(中断候选按估算口径) - 分支管理:软删除(deletedAt + 保留期默认 30 天可配置 + 级联软删/恢复整棵子树——修复 prune 静默移除 live 子孙的数据丢失高危)、重命名(renameBranchCandidate 仅改 label)、修剪(pruneDeletedBranches 过期节点+子树物理清理)、purgeBranchCandidate 彻底删除(幂等化)、getDeletedBranchCount 与 prune 孤儿口径统一;设置页新增
BranchCleanupSettings.vue区块(软删数量/一键清理/保留期输入,三语文案);switch 目标到 root 链上软删节点校验返回 BRANCH_OPERATION_CONFLICT - 性能基准:
test/benchmark/(.benchmark.ts 后缀 + --testMatch 显式运行,普通测试不执行)——2000 文件快照创建 0.3s/恢复 0.7s、1 万条历史 append 0.9s/读取 27ms/用量 12ms、100 候选图操作全亚毫秒(含漂移恢复测量与 102 层深链场景,smoke 上限按实测 10-20× 收紧,harness 输出 GC 可用性并做 JIT 预热) - 分支-存档联动:
bindWorkspaceCheckpoint(节点绑定 workspaceCheckpointId/workspaceState,工具执行存档点 fire-and-forget 接线);切换双模式(chat-only/chat-and-workspace,编排:安全校验→dirty 闸门→取消流→预览→恢复(失败不切分支)→切换,锁序「恢复先于切图」);dirty 文件拦截(WorkspaceRestoreGuard 统一拦截普通恢复与切换,未保存内容不再被静默丢弃);判据富化(hasWorkspaceState/wroteToWorkspace)+ 前端双按钮确认(三语);引用计数清理(checkpointRefCounts 扫描 + deleteCheckpointsByNodeIds 三重闸门 + purge/prune 联动,软删不触发);不做内容哈希去重(增量链共享 4 测试固化);一致性矩阵 26 场景盘点;核实旧存档 manifest 兼容与 ignorePatterns 合并口径已覆盖 - 大文件拆分(落地):
CheckpointManager2413→1687 行,恢复侧文件操作与辅助平移至CheckpointRestoreService(722 行,prepareRestore/legacy 恢复/过滤/哈希收集/排除说明等 12 方法)与WorkspaceEditorRefresher(94 行);CheckpointSettings.vue3284→2263 行,script 拆为 5 个 composable(useCheckpointConfig/useCheckpointExclusion/useCheckpointCleanup/useCheckpointOperationProgress/useCheckpointManifest);SettingsManager2347 行拆为门面 + SettingsCore + 14 个主题服务,settings/types.ts2419 行拆为 11 个主题类型文件 + 聚合入口(公共导出零变化,纯重构)
Added
- 新增共享行级 diff 算法与虚拟行组件测试,以及结构化/unified diff 应用回归测试,覆盖大文件预算降级、公共边缘裁剪、顺序依赖与 relocated hunk fallback
- 新增 agent 间消息通信(用户设计):
agent.sendMessage工具 + 内存 mailbox(会话限定 + 已知 runId 防冒充、threadId + hopDepth 5 跳防循环)——收件方在最近一次工具调用结束后把 inbox 消息追加到工具结果之后随结果返回(主会话工具循环 5 处调用点与子代理执行器均接入),run 结束自动清理;配套信箱与注入点测试 34 用例;用户消息插入与对话删除接线作为二期方案 - 新增树状分支底座(初版):
backend/modules/conversation/branch/——BranchGraph.ts纯函数模块(insertNode / rerollCandidate / editCandidate / activePath / rebuildActivePath / childrenIndex / validate,含环检测)、BranchGraphRepository.ts(branches.json 原子写、损坏抛 BRANCH_STORAGE_CORRUPT、删除对话清理)、types.ts(ConversationBranchGraph / ConversationBranchNode / BranchErrorCode;functionResponse 不独立成节点、kind 含 exported、单 parentId 索引 + activeChildId);配套纯函数与仓储单测 61 用例 - 审计修复核对与测试补齐:确认 xmlFormatter 防御配置、toolResponseFormatter 部分成功序列化、SubAgentRegistry.isEnabled 修复已在提交 3bfab33 就位;
promptToolParser.test.ts补齐 DOCTYPE 实体不展开与超深嵌套拒绝两项安全用例;新增toolResponseFormatter.test.ts(文档 5.6 节 10 项全覆盖)与subagentRegistry.test.ts;各修复批次新增回归测试(损坏 backupDir 删除拒绝、索引回退不断链、manifest LRU、锁排队取消、大小写绕过、深合并保留、用量并发、双 rename 读一致性、删除复活、设置页保存回滚/加载失败等) - 本轮新增测试:子 agent 嵌套(15 例:深度限制/父过滤传播/级联清理)、用户消息插入(8 例:忙时投递/频率限制/失败回退)、分支接线(BranchService 23 + BranchHandlers 6 + BranchGraph 扩展 5)、迁移与完整性(BranchMigration 15 + integrityCheck 32)、复审修复(用量重建/队列超时/重读校验/原子化 9、agentInbox 剥离 11、错误码与重试 8、前端 UX 9、嵌套权限逃逸回归 等)
Fixed
- 修复 SubAgent Monitor 面板抢走 diff 预览位置(用户反馈):
vscode.diff不带 viewColumn 时在「当前活动编辑器组」打开,焦点在 Monitor(Beside 列)时 diff 会开在 Monitor 旁边而非主聊天侧;现在 Monitor 路由上下文下发diffViewColumn(经 tabGroups 按 viewType 定位主聊天所在列,侧边栏时回退主区域第一列),openDiffView显式传viewColumn——无论焦点在哪,diff 都固定跟随主聊天所在列;HandlerContext新增diffViewColumn字段,主聊天自己发起的 diff 行为不变 - 修复前台 SubAgent 转后台(detach)机制的三处残留缺陷:① 循环顶部/工具执行前/启动检查三处取消判定仍裸读父 abortSignal,转后台的 run 在后续迭代或工具调用前仍会被旧流 abort 杀死(绝大多数真实场景失效)——统一改为
parentAbort()(detached 后视为无父信号);② 排队期间 detach 后,acquire 成功时启动检查与超时桥(对已 abort 信号注册的 onParentAbort)仍会杀掉 run——超时桥注册加!detachedFromParent保护;③ detach 把 acquire 桥的 run 控制信号监听一并移除,排队中已转后台的 run 失去 Monitor 终止响应——acquire 桥拆分父信号/控制信号两部分,detach 只摘父信号;新增回归测试(多轮迭代继续执行、排队-detach 继续执行) - 修复用户发消息时前台子 agent 被连带杀掉(用户实测):此前新流启动(StreamAbortManager.create)会 abort 旧流,而前台 SubAgent 的 abort 信号挂在主会话工具循环上,旧流取消会连带终止还在干活的子代理;现在
SubAgentRunController增加attachedToParent标记与detachFromParent(转后台)能力——StreamAbortManager.create在 abort 旧流之前先把该会话活跃前台 SubAgent 转为后台(executor 同步解绑父 abort 信号的组合监听、排队唤醒桥与超时桥),run 继续执行至完成,结果经 Monitor/事件总线呈现(广播run_detached事件);后台 run(attachedToParent=false)不受影响,保留 TaskManager 取消能力;新增回归测试 11 例(runController detach 语义 5、StreamAbortManager 转后台矩阵 4、executor 集成含对照组 2) - 修复后台子 agent 完成消息被截断(用户实测):
buildSubAgentSection此前把后台 SubAgent 最终报告按 4000 字符截断,并以[Truncated N more characters. Open Monitor to view the full transcript.]收尾,而 Monitor 是人类 UI、主模型没有访问路径,研究/审查报告被腰斩(前台不传 background 的回复完整,因 functionResponse 无截断);现在回执完整内联结果正文,与前台载荷同规格——完整结果本就要经 postMessage 转发给 Monitor(现状无截断),回执再经 chatStream 与普通用户消息同路径发送,无新增载荷上限;新增回归测试锁定超长报告不再截断 - 修复 manifest 缓存无界与锁队列不可取消:manifest 内存缓存改 LRU(上限 32)且
getManifest读后即弃;工作区锁排队等待响应取消信号(abort 时移出队列);同 owner 超集重入改为运行时 fail-fast 而非静默排队死锁 - 修复存量并发与一致性缺陷:RetentionService 清理以删除返回值为准、对多依赖者循环合并;updateSummary 不再多余写入
updatedAt(列表排序不再抖动);loadCheckpoints失败保留旧值;trimWindowFromTop窗口裁剪不再永久丢弃窗口外检查点;恢复确认框取消时清理选中残留;前端restoreAndEdit失败补重载兜底 - 修复恢复/预览性能残留:
collectCurrentWorkspaceState与 legacy 恢复哈希从顺序读盘改为共享模块有界并发流式哈希;previewRestore经runExclusive({ needFileLock: false })只取工作区级互斥,不再持有全局文件写锁阻塞全部写工具 - 修复恢复顺序与进度:恢复改为「先复制(含哈希校验)→ 全部成功后最后删除」,复制失败不再留下「已删未补」中间态;删除阶段逐文件上报进度,进度 total 覆盖删除 + 恢复全量
- 修复复审轮发现的高危问题:用量索引全量重建改为队列内基于最新盘面执行(并发 main 条目不再被覆盖丢失);
agentInbox在落盘前剥离(不再随历史每轮重放给模型);错误条重试白名单并入后端真实可重试错误码(API/NETWORK/TIMEOUT/PARSE,排除 CANCELLED/CONFIG/VALIDATION 与 RESTORE_*——引入的重试回归修复);只读预设嵌套派发权限逃逸修复(嵌套继承父级工具过滤、无写工具 agent 移除 subagents、空工具集拒绝一切);retryFromMessage/editAndRetry删除或编辑 IPC 异常路径不再继续重试(历史不重复、本地与后端一致);rejectToolCalls读改写原子化;deleteConversation纳入会话写锁(删除复活闭环);段读取后重读 index 版本比对(双 rename 窗口错位历史被检出);enqueueWrite挂起超时;统计缓存回填含 subagent 合并条目