Repository navigation
Conversation
🤖 Open Code ReviewTarget: PR #777 🔍 OpenCodeReview found 115 issue(s) in this PR.
1.
|
|
| user_memories: | ||
| "OLD.deleted_at IS NOT NEW.deleted_at OR (OLD.status='deleted') != (NEW.status='deleted') OR OLD.user_id IS NOT NEW.user_id", | ||
| raw_turns: | ||
| "OLD.deleted_at IS NOT NEW.deleted_at OR OLD.redacted_at IS NOT NEW.redacted_at OR OLD.user_id IS NOT NEW.user_id OR OLD.episode_id IS NOT NEW.episode_id", |
There was a problem hiding this comment.
后台完成新轮次归属会使正在浏览的分页失效
问题: 用户停留在记忆管理第2页时,另一个正常会话完成一轮问答,后台把这条新轮次首次归入任务,已有浏览视图就会失效。下一次页面检查或翻页返回409;前端会清空详情并重新建立视图,翻页也可能先显示错误。这与本 PR 约定的“普通新增仅提示有更新,由用户点击原刷新按钮重建视图”不符。本轮用合成数据、模型替身和真实本地 HTTP 路由复现:已有25条记录的第2页先返回200,新轮次归属完成后,同一视图返回 Panel view expired; refresh。这不是桌面人工验收结果。
原因: 这里把所有 episode_id 变化都当成需要撤销旧内容访问的事件。现有异步采集流程先保存 episode_id=NULL 的原始轮次,后台 SessionTurnService 调用 bindRawTurnEpisode 完成首次归属时也会触发它,从而更换全局安全标记。PanelBrowse.get 随即拒绝所有旧视图;usePanelViewStatus 收到409后清理详情、重置视图并重新读取。正常新增因此走进了隐私失效流程。
建议做法: 区分新轮次首次归属与已有记录的迁移、删除、脱敏及所有者变化。首次归属应保留既有列表成员、页码和已读内容,只更新“有更新”提示;真正影响已有内容可见性或归属的操作仍须立即清理缓存。补充通过正常轮次完成入口、后台归属、页面检查和第2/3页切换的回归,同时验证删除、脱敏、跨用户变更及已有轮次重新绑定仍会失效,不能直接取消所有 episode_id 变化的保护。
|
|
||
| export const SCHEMA_VERSION = 13; | ||
| export const SCHEMA_MIGRATION_ID = "013_embedding_retry_lease_counters"; | ||
| export const SCHEMA_VERSION = 14; |
There was a problem hiding this comment.
升到 schema14 后,旧租约迁移测试的三个用例失效
问题: 本次将数据库版本升为14后,现有 Memory/tests/repository/embedding-retry-lease-counters.test.ts 有3项失败,准确 base 上对应3项通过。两项旧库升级测试仍期望13和 .pre-v13.bak;另一项失败回滚测试仍用 WHEN NEW.version=13 注入错误,当前迁移写入14后不再触发,无法验证失败时保留租约和原库。正常执行 Memory 测试因此会失败,也不能据此声称现有租约迁移回归已全部通过。
原因: 该迁移实现最终记录的是当前 SCHEMA_VERSION,不是依次插入每个历史版本;升版同时改变了最终版本、备份名和失败注入点,但这份已有回归没有同步适配。本轮仅在诊断副本中更新这三个契约点,保留租约、重试计数、备份和事务回滚断言后,文件8项全部通过;原提交中的3项失败仍然存在。
建议做法: 同步更新该测试,按当前版本构造预期备份路径和失败触发条件,继续验证历史租约计数恢复、旧持有者不能完成任务、失败迁移不留下半成品以及重试保留原备份。修正后运行原始测试文件和13→14迁移/回滚套件,不能删除失败注入或弱化已有数据断言。
| params.push(id, id.length + 2, `::${id}`); | ||
| } | ||
| const base = conditions.join(" AND "); | ||
| const total = Number( |
There was a problem hiding this comment.
日志每次翻页都重新校验全量记录,导致约九秒等待
问题: 同一份本机数据的隔离副本中,日志接口首页由基线的54 ms变为9794 ms;隔离浏览器页面第2/3页分别等待8665/8807 ms,刷新等待8325 ms。返回内容因新增隐私过滤有所不同,但每次只看20条日志也需要约九秒,已经违背此次加载优化目标。
原因: 每页先对所有候选日志展开 JSON 引用并逐一检查关联记录是否可见,再执行带同样条件的分页查询。SQL 拆解中第2页计数耗时4652 ms,列表查询另耗时4314 ms;执行计划还需要临时排序,LIMIT 并未把前置校验限制在20条。
建议做法: 保留删除、脱敏和归属校验,把日志引用关系及列表摘要做成可索引的轻量数据,按隐私失效规则维护计数或缓存;分页读取避免重复全量 JSON 校验。保留用户已确认的“在当前工具/来源范围内搜索全部日志”的语义,不能退回只筛当前页来换取速度。用相同规模数据覆盖首页、第2/3页、筛选和刷新,同时验证删除/脱敏后旧日志内容不能重新出现。
| } | ||
| return []; | ||
| } | ||
| async function prepare(parts: PanelCachePart[]) { |
There was a problem hiding this comment.
首次建立统计缓存仍需约 8.6–10.2 秒
关联:同文件 55–58 行、http.ts:607–615、panel-cache.ts:101。
问题: 概览和分析现在共用统计结果,持久缓存也改善了再次启动;但首次统计缓存尚未准备好时,用户仍可能长时间等待。在约 2.74 GB、47,482 条记忆的隔离副本上,从首次概览 HTTP 请求到统计真正 ready,三次独立初建测得 8640、9730、10214 ms,其中第三次来自真实 schema13→14 升级副本。缓存已建成后的重启约 1050 ms。这些初建测量没有先用 health 触发预热,证明的是初建成本,不能据此断言真实桌面一定到打开页面才开始。基线本身也慢,本项属于首次加载优化目标尚未完成。
原因: worker 在读取统计快照前,必须补齐全部待处理的 memories/metrics 缓存,历史 JSON 提取和派生数据写入仍在首个结果的等待路径上。当前已有异步预热,但由带有效认证、UID 和时区的 health 请求触发,而不是服务可用后主动启动;触发条件未满足,或者用户在预热完成前进入页面,仍需等待。worker 避免了在 HTTP 主线程执行这些重查询,但不能消除 CPU、磁盘及数据库写锁竞争,快速返回 preparing 也不代表加载完成。
建议做法: 数据库正常打开、服务开始接收请求后,主动在后台准备本地面板缓存,不等待用户打开页面,不依赖登录 UID;日期统计使用设备已确认的时区配置。复用持久缓存,只补缺失和变化的数据;普通重启不重做全量历史回填,页面请求与预热共享同一份工作。
继续使用 worker 分批处理,缓存写入采用短事务;前台读取、采集和召回繁忙时让出资源,保留已完成批次,恢复后继续,服务停止时取消并等待退出。统计完成后一次替换快照,普通新增由后续增量处理,避免持续写入使整轮计算反复重来;删除、脱敏等可见性变化仍立即使旧结果失效。本 PR 保持原有 UI,不增加更新红字、提示块或统计时间行。
验收覆盖升级后立即打开页面、预热完成后打开、缓存已建立后的重启及原刷新按钮,计时到实际内容可用;另在缓存建立期间并发执行 health、采集和召回,核对响应时间与成功率。若立即打开仍需等待近 10 秒,还须减少首个完整统计所需的历史正文/JSON 处理量,不能仅把计算提前或移到后台就宣布解决。现有性能证据禁用了模型执行,不替代这一并发验收。
| serverTime: string; | ||
| } { | ||
| return this.panelReadModel.panelItems(this.withTimeZone(input)); | ||
| return this.panelReadModel.panelItems(this.panelInput(input)); |
There was a problem hiding this comment.
设备级读取已实现,但遗漏了旧账号隔离测试
应同步修改:user-memory.test.ts:942–973,重点 964–973 行。
问题: 本地面板已经按约定改为同一设备跨账号读取同一份记忆,但现有 User Memory 测试仍要求换账号后列表为空、概览数量为 0。当前 head 的 Memory 全量测试因此有 1 项失败;准确 base 的原文件 37 项通过。前端 UID 绑定和原 Computer History 导航失败已修复,这条剩余意见针对遗漏的单测,不是要求恢复本地账号隔离。
原因: 本地 panelInput() 现在会移除读取条件中的 userId,列表和概览都按设备范围读取;旧测试的名称及“另一个账号不可见”断言没有随这次语义变化更新。列表断言失败后,同一用例后面的概览断言也没有执行。
建议做法: 把该用例改为验证本地设备读取契约:切换账号后仍可见同一条记忆,列表、搜索和概览数量一致;保留 NULL UID 新记录和历史 UID 记录的回归。云端按账号授权、写入归属及服务访问认证继续由独立用例验证,不能为了变绿删掉这些保护。运行原测试文件及 Memory 全量套件,确认没有其他旧断言继续依赖已移除的本地 UID 过滤。
|
本轮更新已推送到本 PR,提交: 修改内容
验证与性能边界Memory 全量 2,790 项通过、2 项跳过;Desktop frontend 468 项通过;定向恢复及交互回归 15 项、runtime/startup 回归 7 项通过;typecheck、lint、Memory 构建及 DMG 校验通过。打包 UI 使用 46,891 条记忆、12,000 条日志的合成库,没有使用真实用户数据或调用模型。打包测试中的 503 恢复采用组件级 fetch 注入,没有对已打包应用注入真实 503。 同一测试库上,首次概览 UI 中位数从基线 2,861.6 ms 降到最终包 377.9 ms;但最终包的首次点击发生在服务 ready 后约 11.1 秒,后台预热已有时间完成。隔离统计测试中,冷缓存完整快照为基线 2,002 ms、最终 2,090 ms;已有派生缓存后约 233 ms 和 232 ms。因此本提交改善的是预热完成后的页面等待,冷快照计算本身没有变快。该测量库约 1.30 GB,与 review 中约 2.74 GB 的样本不同;目前没有“服务刚 ready 就立即打开页面”的打包计时,不能据此认定该冷路径已解决。 若验收要求是服务 ready 后立即打开也能快速显示,需要继续减少概览冷路径的实际计算,并在同一大库上补做即时打开计时。 |
|
|
| async function prepare(parts: PanelCachePart[], backgroundId?: number) { | ||
| let batches = 0; | ||
| for (const part of parts) | ||
| while (fillPanelCacheBatch(cacheWriter, part, part === "logs" ? 64 : 128)) { |
There was a problem hiding this comment.
缓存预热与轮次采集争写数据库,导致采集失败
关联:session-turn-service.ts (line 1567)。
问题: 服务启动后回填面板缓存时,原生轮次采集接口可能返回 500 database is locked,本次轮次没有保存。在约 4.75 万条记忆的隔离库上,当前 head 开启预热时 8 次采集全部失败,新增原始轮次为 0;关闭预热后 8 次全部保存,准确 base 同样 8 次全部保存。验证使用实际 HTTP、SQLite 和缓存 worker,关闭了后台任务执行,不等同于真实 Hook 客户端验收。
原因: 新增缓存连接与采集事务写入同一个数据库。采集先读取去重和会话状态,再写入会话时发生锁冲突,实际错误为 RuntimeRepository.createSession 抛出 SQLITE_BUSY。现有繁忙判断只看任务和模型计数,没有协调原生采集事务与缓存写入;仅缩小缓存批次或在写入后检查繁忙状态,不能防止冲突。
建议做法: 在事务开始前统一协调缓存与采集的写入,等待发生在事务外,缓存以短批次让出写入机会,避免同步等锁阻塞 HTTP 主线程。遇到仍可能发生的瞬时锁冲突,完整回滚后有上限地重试整个可幂等事务,保留原生轮次去重,不能吞错后返回成功。覆盖首次预热、增量回填期间持续采集,以及重复请求、取消和停机;验证每个成功响应都对应已保存且不重复的轮次,健康检查保持响应。
| if (message.yield && isStatisticsMethod(active.method)) { | ||
| const resume = () => { | ||
| if (this.closed || this.worker !== worker || this.active !== active) return; | ||
| if (this.backgroundBusy()) { |
There was a problem hiding this comment.
后台持续有任务时,概览统计可能一直等待,切到列表后才恢复
关联:memory-service.ts (line 345)。
问题: 概览已经加载过,统计因删除或归属变化失效后,如果后台持续有任务,再次进入概览可能一直加载。真实 HTTP/worker 的合成测试中,加入约 600 条记忆、使旧统计失效,并持有一个调度许可模拟等待远端模型的任务:连续 32 秒没有统计结果,健康检查正常。随后读取原始记忆列表耗时 252 ms,概览再过 30 ms 就绪,此时后台许可仍未释放。测试证明调度缺陷,不等同于逐请求还原本机那次现场。
原因: 任意后台任务运行都会暂停统计,等待远端模型也被算作繁忙。暂停时清除了读取超时计时器,之后每 25 ms 检查一次,却没有保证统计继续执行的机制。概览请求只查询准备状态;列表请求优先级更高,能中断统计并通过另一条路径补齐缓存,所以切页会改变结果。
建议做法: 保留后台预热和已完成批次,但用户正在等待概览或分析时,应保证同一份统计工作能够持续推进,不能要求后台任务全部结束。按实际数据库资源协调短批次执行,区分写入占用与等待模型,兼顾采集优先和统计的执行机会;将等待纳入总耗时控制,并支持取消和停机。统计完整生成后一次发布,删除和脱敏后不显示失效旧内容。覆盖后台任务连续运行、慢模型、统计失效后重新进入及并发采集,验证用户停留在概览也能自动显示结果,保持原有 UI。
| }, | ||
|
|
||
| async panelMemory(id,context){ | ||
| return request("GET","panelMemory",GetMemoryOutputSchema,{params:{memoryId:id},context,maxRetries:0}); |
There was a problem hiding this comment.
原始记忆详情丢失原有超时重试,短暂失败后必须重新点击
关联:memory-runtime-client.ts (line 223)。
问题: 打开原始记忆详情遇到一次短暂超时后,即使服务已经恢复,页面仍显示 memory layer timeout,需要重新点击。实际 React 组件和前端客户端测试中,注入一次 503 后恢复正常响应,等待 60 秒仍未重试,再点一次才恢复。后端对照中,相同一次超时,原详情调用自动重试成功,新详情调用只请求一次就失败。
原因: 本 PR 将详情改为调用新接口 panelMemory,其后端显式设置 maxRetries:0,移除了原 getMemory 的有界重试;前端又直接请求,没有经过已有的共享重试队列。详情组件捕获异常后只显示错误,两端都没有自动恢复。
建议做法: 保持原有 UI,在统一读取层为详情加入有上限、带退避的瞬时超时/503恢复,避免前后端叠加重试。关闭详情、切换记忆、隐藏或离开页面后停止不再需要的请求与重试,迟到结果不能覆盖新详情;授权失败、删除和失效结果仍按原规则处理,不能展示失效正文。覆盖单次超时后自动恢复、持续失败后的有界退出、取消及迟到响应。
There was a problem hiding this comment.
本轮最终实现已调整为统一由前端共享队列承担新详情接口的瞬时失败恢复(2986df70、9e783083),更新此前“新详情在后端重试”的说明。panelMemory、panelLogDetail 后端均保持 maxRetries: 0,前端最多 4 次请求、250/750/1500 ms 退避,避免两端重试相乘。旧服务不支持面板路由时的 getMemory 兼容回退仍使用原后端有界重试,并放在前端重试包装之外。
授权失败、删除和失效结果不作为瞬时故障重试。关闭、改选或离开记忆子页会取消对应订阅,页面重新可见后按仍有效的选择恢复读取,旧响应不能回填正文。用户记忆等详情同样复用共享队列;没有重新加入手动重试按钮。已覆盖单次 503 恢复、持续失败达到上限、取消和迟到响应;前后端相关测试通过,最终前端全量 3421 项通过、3 项跳过。
| let resumePreparation: (() => void) | undefined; | ||
| async function prepare(parts: PanelCachePart[], backgroundId?: number) { | ||
| let batches = 0; | ||
| for (const part of parts) |
There was a problem hiding this comment.
首次概览仍需约 10 秒,重启未复用完整统计快照
关联:panel-cache.ts (line 156)、stats.ts (line 26)、index.ts (line 209)。
问题: 服务已经能主动预热,但用户在服务可用后立即进入概览,仍需等待首次统计完成。在约 2.74 GB、47,482 条记忆的独立升级副本上,统计就绪耗时 10,763 ms,实际 Chromium 页面显示内容耗时 9533 ms;预热完成后请求仅需 10 ms,已有底层缓存的服务重启约 1293 ms。本组关闭后台任务并使用模型替身,证明首次准备成本仍未解决;不能将首次升级的约 10 秒外推成每次重启的耗时。
原因: 首份统计返回前,worker 仍须补齐全部待处理的记忆和指标缓存,历史 JSON 提取和派生数据写入都在等待路径上。数据库中的底层派生缓存会跨重启保留,但最终概览/分析统计快照只存在服务内存里,重启仍需重新生成。当前预热在服务可用后启动,启动加载阶段没有以面板必要数据就绪作为协调条件。提前启动或快速返回 preparing,都不能代替减少计算量、复用有效结果。
建议做法: 保持原有 UI,按以下方案完成首次准备及重启复用:
- 减少首次工作量。 概览首次显示只依赖必要的轻量统计数据,避免等待不相关的历史缓存全部准备完成。后续只处理变化记录,详情正文按需读取;缓存采用短批次写入,与采集事务协调,同时保证用户等待的统计能够推进。
- 复用完整统计快照。 每次完整生成后原子保存小的概览/分析统计结果及其对应的数据版本,重启时验证兼容性、数据库归属、日期/时区和隐私有效性,再直接恢复并增量更新。按缓存字段及统计规则决定兼容性,不按每次 App 版本变化清空;普通重启、兼容升级及保留数据目录的覆盖安装应继续复用。删除、脱敏后禁止展示失效旧结果,普通新增不应不断触发全量重建。
- 利用现有启动阶段准备。 数据库完成必要迁移、服务可响应后,立即在 worker 中恢复或更新缓存,与其他启动工作并行;缓存规则不兼容时优先准备首屏必需部分,保留已完成批次以便中断后续做。启动页如需等待,应有明确上限及失败处理,其余历史回填继续后台执行,不能让整个 App 等待全量缓存,也不能把缓存未就绪判成服务不可用。
| if(props.log.detailAvailable&&props.client?.getPanelLogDetail) void props.client.getPanelLogDetail(props.log.id).then(result=>{if(active)setData(result.log);}).catch(error=>{if(active)setError(toErrorMessage(error));}); | ||
| return()=>{active=false;}; | ||
| },[props.client,props.log.id,retry]); | ||
| if(error)return <div role="alert">{error}<button type="button" onClick={()=>{setError(undefined);setRetry(value=>value+1);}}>{t("memory.tasks.loadContent")}</button></div>; |
There was a problem hiding this comment.
删除日志和用户记忆详情新增的“继续读取内容”按钮
关联:user-memories-sub-page.tsx (line 201)。
问题: 展开日志详情或打开用户记忆详情时,如果内容读取失败,页面会新增“继续读取内容”按钮。日志中按钮紧贴错误文字,用户记忆中按钮位于错误框下方。这两处新增操作不符合本次保留原有 UI 的要求。
原因: 将详情改为按需读取时,两个错误分支分别新增了手动重试按钮;它们不是原有界面的控件。
建议做法: 删除这两处新增按钮,保留原有布局和操作入口。按需读取及瞬时失败恢复仍需正常工作,不能仅移除按钮后让内容停留在无法恢复的错误状态。覆盖详情成功、单次超时恢复及关闭后重新打开,确认两处按钮均不再出现。
| return <>{(logs.length > 0 || legacy !== undefined || (!busy && !error)) && <SkillTimelineSection entries={legacy??skillTimelineFromLogs(logs,props.skillId)} />} | ||
| {busy&&<MemoryStateBox message={t("common.loading")} />} | ||
| {error&&<p role="alert">{error}</p>} | ||
| {(cursor||error)&&<button type="button" disabled={busy} onClick={()=>void load(cursor)}>{t("memory.tasks.loadContent")}</button>} |
There was a problem hiding this comment.
技能时间线的“继续读取内容”按钮应统一样式并放入时间线容器
问题: 技能演化时间线正常加载前 20 条且还有后续记录时,“继续读取内容”按钮显示在时间线卡片外、SKILL.MD 内容卡片之前,样式近似普通文字,既不符合 Memmy 整体按钮风格,也不清楚属于哪个内容区域。
原因: 当前按钮没有应用项目既有按钮样式,并与时间线组件并列渲染,落在时间线容器之外。
建议做法: 保留该按钮及分批读取功能,复用 Memmy 既有同类按钮样式,将按钮放在时间线容器内部、当前记录末尾。加载中的禁用状态及失败后的再次读取保持有效;验证超过 20 条、加载中、读取失败和全部加载完成时的位置、样式与行为,避免影响下方 SKILL.MD 内容区域。
|
@origami-chen 本轮返修已推送,当前提交为 1f6b006e,包含 27849db 和 3c05edc,并保留远端已有的技能统计和空列表分页修复。以下按六条意见说明实际进展。
另外确认:此次新增的前端共享自动重试逻辑已撤回;现有请求合并和订阅取消机制保留,不能将其描述为删除整个共享请求队列。 验证情况:最终合并提交完成 typecheck、lint 和 Memory build。相关回归首轮覆盖 80 个文件、704 项测试,703 项通过、1 项 HTTP 大样本采集测试超时;该文件单独复跑 2/2 通过。没有将这描述成一次完整全绿运行,也尚未完成最终打包桌面验收。 同数据副本的性能对照是 9f31fba → 3c05edc(远端合并前),使用约 1.30 GB、46,891 条记忆和 12,000 条日志的合成库,10 轮 AB/BA 交替、相同依赖和条件,关闭后台任务与自动预热,统一预热文件系统缓存,测量 HTTP 统计完整就绪:
40 次统计结果一致。首次改善约 3.8%,主要收益在有效快照的重启复用;这组数据不是 Chromium 首屏耗时,也不是对原评论 2.74 GB 数据副本约 10 秒问题的复现,不能据此宣称首次慢的问题已完全解决。 |
|
| // Native scans have no Hook envelope. Use the configured owner only when | ||
| // the request (including authenticated scope) did not provide one. | ||
| const response = await this.acceptCapture(() => this.withModelTaskContext(() => this.sessionTurns.withEpisodeSelection( | ||
| const response = await this.panelWrites.run(() => this.acceptCapture(() => this.withModelTaskContext(() => this.sessionTurns.withEpisodeSelection( |
There was a problem hiding this comment.
缓存写入协调遗漏 Memmy 的轮次完成接口,预热仍会导致采集失败
状态:部分修复,仍有效。
关联:completeTurn:1714 (line 1714)、reader-worker.ts (line 34)、hook.ts (line 447)。
问题: 服务启动回填面板缓存时,Memmy 自身通过 POST /api/v1/turns/:id/complete 保存轮次,仍可能返回 500 database is locked,失败请求没有保存对应轮次。用 6,000 条合成记忆、实际 HTTP/SQLite/worker、明确的模型替身补测,开启预热时 32 次请求中 20 次锁冲突失败,仅 12 条落库;同一 head、相同模型替身关闭预热后,32 次全部保存。这里验证的是服务接口;实际 Agent 还有有限重试,不能将单次接口失败直接写成客户端最终永久丢失,但持续回填会反复触发失败。
原因: 新增 PanelWriteCoordinator 只包住了 completeSourceTurn,即原生来源轮次入口。旁边的 completeTurn 仍直接进入先读后写的事务,没有与缓存 worker 协调写入;Memmy 的记忆 Hook 实际调用的正是这个入口。缓存连接与该事务竞争 SQLite 写锁时,服务返回失败。原生来源接口的并发测试通过,并不能覆盖 Memmy 自身的轮次保存。
建议做法: 将缓存回填与所有受影响的轮次写入入口纳入同一套事务前协调,至少补齐 completeTurn,并检查开始轮次、工具观察、子任务等共享采集路径。等待必须发生在事务外,缓存每个短批次释放写入机会;瞬时锁冲突完整回滚后再有界重试可幂等事务,保留原有去重和取消、停机行为。使用原生来源接口与 Memmy 轮次接口分别覆盖首次预热、增量回填、重复提交、取消和停机,核对成功响应对应真实落库、不重复,health 保持响应,不能只靠客户端重试掩盖服务端冲突。
There was a problem hiding this comment.
已在 9e2e5c8、9e783083 补齐。写入许可放在同步写事务之前获取,completeTurn 与 completeSourceTurn 共用协调;同时检查并纳入 startTurn、observeTool、subagentStart/subagentComplete、compact 等相关写事务。等待许可发生在事务外,等待模型时释放许可;模型返回后的纠错持久化也重新取得许可,并以完整事务原子提交,瞬时锁冲突完整回滚后有界重试,保留取消及停机退出。
验证使用真实 HTTP、SQLite 和缓存 worker,模型使用替身:6,000 条初始合成记忆及新增 600 条回填阶段,分别覆盖原生来源每阶段 8 次、Memmy 轮次每阶段 32 次请求;不依赖客户端重试,核对成功响应实际落库、重复轮次返回同一 ID、health 返回 200。另有事务准入/回滚、取消、停机及模型等待释放许可测试。本地独立复核的并发采集与后端 HTTP 客户端两套测试共 38 项通过。
边界说明:这里验证的是服务接口与事务协调,并非真实 Agent Hook/桌面端验收。auto-cr 另发现旧有 observeTool 整体调用幂等问题:原始观察已提交,而后续纠错持续失败后整次重发可能重复观察;本轮未扩展修复这一既有问题,不能据此声称整条工具观察调用已完全幂等。
| usePanelPoll(Boolean(props.client) && !props.client?.checkPanelView, () => refresh(page, sourceAgent, { useCache: false })); | ||
| const detailRequestIdRef = useRef(0); | ||
| const detailAbortRef = useRef<AbortController | null>(null); | ||
| useEffect(() => () => { |
There was a problem hiding this comment.
原始记忆详情加载中切走再返回,会一直停在加载中
关联:详情读取:246 (line 246)、memory-page.tsx。
问题: 点开原始记忆详情,在请求完成前切到其他记忆子页,再返回原始记忆页,详情抽屉仍然显示“加载中”,不会自动恢复,需要关闭后重新点击。补充组件测试中,请求已被取消,返回后推进 60 秒仍只有第一次调用,抽屉保持加载;相同操作在上一轮 head 能显示已完成的详情。
原因: 子页面切换后仍保留挂载和状态。新增 effect 在可见性变化时取消详情请求并递增请求编号,却没有清理加载中的详情状态,也没有在重新可见时恢复选中记忆的读取。原请求的成功和失败都会因编号或取消检查被丢弃,因此保留下来的加载状态没有任何后续请求能够结束。
建议做法: 保持原有抽屉 UI,并补齐中断后的状态恢复:切走时停止请求,返回后为仍选中的记忆重新读取,并重新核验内容可见性;如果用户已经关闭抽屉或选择其他记忆,不再恢复旧请求。覆盖加载中切换子页、离开后返回、连续选择不同记忆及删除/脱敏后的返回,确认不会永久加载,也不会显示迟到或失效正文。
There was a problem hiding this comment.
已在 2986df7、9e783083 补齐这条恢复链,保留原抽屉 UI。切到其他记忆子页时取消当前详情订阅、使旧请求失效并清理旧正文/加载状态;保留当前选择,返回后仅为仍选中的记忆重新读取并重新校验可见性。关闭抽屉、改选其他记忆或切换 client 后,不会恢复旧请求,迟到结果不能覆盖新选择。
这不是只恢复共享队列就能解决的问题:原缺陷还包括页面取消后没有重新发起读取,本轮同时修复了页面状态和请求生命周期。组件回归覆盖加载中离开再返回、连续改选、关闭、迟到响应及返回后读取失败/不可见的处理;最终前端全量 3421 项通过、3 项跳过。
| if (task.started || task.subscribers.size === 0) continue; | ||
| task.started = true; | ||
| this.active++; | ||
| void Promise.resolve() |
There was a problem hiding this comment.
删除额外按钮不应撤回列表的自动恢复能力
本次要求是删除日志详情和用户记忆正文读取失败时新增的手动重试按钮,保留原有 UI 和自动恢复能力。但同一轮修改还撤回了前端共享请求队列的自动重试,导致打开经验等列表、翻页或点击原刷新按钮时,只要第一次请求短暂返回 503,即使服务随后恢复,页面也不会自动再读取:首开停在错误状态,翻页或刷新无法完成本次操作。
There was a problem hiding this comment.
已在 2986df7 恢复前端共享请求队列的有界自动重试,首开、翻页及原刷新操作遇到瞬时网络错误或 502/503/504 后可自动恢复;持续失败最多请求 4 次,退避为 250/750/1500 ms。日志详情和用户记忆详情新增的手动重试按钮仍保持删除,原有 UI 不变。
9e78308 将新详情读取也统一交给前端共享队列,避免前后端叠加重试;7876950a 补齐视图检查的取消链:切到其他记忆子页后撤销该页订阅及后续重试,返回后重新检查,不影响仍有效的其他订阅,也不接受迟到结果。
已覆盖首开/翻页/刷新单次 503 恢复、持续失败退出、切页取消、返回恢复和共享订阅隔离。最终 head 的相关 4 个测试文件共 30 项通过,前端全量 3421 项通过、3 项跳过。
|
本轮修复已推送,当前 head:7876950aad22e0987dbaab278b8f7451cb201e72。
更新此前回复中的两点:共享队列自动重试现已恢复;新详情接口的瞬时失败重试现由前端统一承担。旧 getMemory 兼容回退保留原后端重试,不叠加前端重试。保持原有 UI,日志/用户记忆详情的额外重试按钮未恢复;增量统计仍暂不修复,本轮未包含该优化。 验证:最终 head 相关 4 个文件 30 项测试通过;前端全量 307 个文件、3421 项通过、3 项跳过;根目录 lint、typecheck 及前端生产构建通过。9e783083 上另经独立运行的真实 HTTP/SQLite/worker 并发采集及后端 HTTP 客户端测试共 38 项通过(模型替身)。这不等同于真实桌面/Agent Hook 验收,本轮未重跑 DMG 验收,也未重新测量首次概览性能。 本地审查另外识别出的既有技能编辑器迟到正文、工具观察整次调用重发幂等、同 ID 日志刷新旧详情问题,按本轮范围未扩展修复;不将这些或首次概览慢问题一并宣称关闭。已在对应评论线程补充修复和验证说明,保留线程供复审。 |
问题与结果
记忆管理在首次打开、切页和刷新时会读取完整向量或大段 JSON,概览与分析重复计算;后台新轮次首次归属还会使正在浏览的分页失效。管理页按登录 UID 过滤的部分入口,也会遗漏历史账号记录或 NULL UID 新记录。
本 PR 改为轻量展示读取、共享统计来源和固定分页视图,并保留原页面布局及聊天展示。管理页统一展示、搜索和统计本机新旧记忆,历史 UID 不改写;管理页的读取范围不用于放宽召回、演化或删除权限。
主要修改
验证
已合入最新目标
81a86226e8cf9ac1c793e5f13b2b4aac005e5fe8,以下为同步后的本地检查;远端 CI 状态以当前提交的 Checks 为准。本次新增针对性断言在修复前有 5 项失败,修复后通过。来源撤销测试同时覆盖列表、全文搜索、总数、完整第 2/3 页、新建视图、统计和重启;保留未修改的 memory 行及普通仓储读取结果,逐行核对管理读取未改写源表。NULL UID 测试同时覆盖正常首次归属、重复绑定、重绑、错误 owner 及隐私失效。
同步前
3e4719b4的隔离浏览器使用生产子页组件、真实 Backend、Memory HTTP 和编译后的 JS worker:53 条记录的第 2 页在 NULL UID 首次归属后保留原 20 个成员和视图;来源脱敏后,通过原刷新按钮重新搜索不再命中旧标题,重新翻页不复现,概览同步变为 52。临时入口和样例仅用于本地验收,不纳入提交。性能与验收边界
以下性能及浏览器证据来自同步前的
3e4719b4阶段,不等同于最终合并提交的桌面/性能验收。同一隔离合成库包含 46,891 条记忆、12,000 条日志,源库约 1.30 GB。最终构建单次 HTTP 实测:
这些是单机描述性样例,未清空 OS 文件缓存,不作为跨版本因果百分比。首次迁移/派生数据构建仍有成本,不将快速返回 preparing 算作完成。最终版本尚未完成 DMG 安装后的验收,也未覆盖真实账号、Hook/召回竞争、受控冷盘及完整跨平台发行矩阵。
分支与范围
沿用现有 PR #777:目标
MemTensor:v1.2.6,修复分支Orsted290:fix-memory-panel-performance-v126,已同步目标81a86226e8cf9ac1c793e5f13b2b4aac005e5fe8。本轮管理读取与视图补修未修改历史数据、召回、演化或删除执行逻辑,未纳入原有未提交的 lockfile 改动。本轮目标分支同步
通过 merge 同步目标分支,保留上游数据库启动阶段、后台准备和公平调度改进。冲突处理同时保留面板只读配置、来源投影函数,以及
storage-startup-worker与panel/reader-worker两个 runtime 入口;演化测试保留双方新增断言。另补数据库准备完成后面板读取与只读写入拒绝的串联回归。