Repository navigation
Conversation
| ] as const; | ||
| const CLEAR_MEMORY_TABLES = [ | ||
| ...BUNDLE_TABLES, | ||
| ...BUNDLE_TABLES.filter((table) => table !== "source_turn_captures" && table !== "memory_project_aliases" && table !== "memory_namespace_aliases"), |
There was a problem hiding this comment.
清空全部记忆后,已有原生会话的新回合无法继续采集
问题
现有设计区分两种删除操作:单条删除记忆后,保留其来源对应的删除记录,后续扫描同一来源的同一回合时,不应重新生成被删除的记忆;全量清空则重置记忆与采集记录,允许重新扫描历史。这里按来源身份识别,不是禁止所有语义相似的记忆。指定 base 的全量清空也会清除此前单条删除的记录,本条不要求新增“清空后仍保留单条删除状态”的行为。
用户清空全部记忆后,继续使用之前已经采集过的 Codex 等原生会话,新回合会返回 pending / source_session_missing,无法写入记忆;重新扫描此前已采集的回合也会被判为 capture_deleted。补充测试使用真实 Memory service 和临时 SQLite:先成功采集一轮,调用 clearAllData(),再提交同一原生会话的下一轮,base 返回 stored,本 PR 返回上述 pending。
原因
这里把记录原生回合采集结果的 source_turn_captures 表排除在全量清空之外,但关联的 sessions、episodes 和 raw_turns 仍被删除,旧记录中的会话编号及缓存结果也没有清理。
提交新回合时,latestSourceTurnCapture() 找到这条旧记录,resolveSourceSession() 随后找不到它指向的 session,于是拒绝继续创建会话;重扫旧回合则命中已删除结果。全量清空与单条删除需要不同语义,当前仍保留的源码注释及原有测试明确要求:全量清空重置采集身份,单条删除保留删除记录。
建议做法
恢复全量清空时同步清理采集记录的约定,保留 source_turn_capture_activated_at 所定义的 Hook 启用时间边界;单条记忆删除继续保留防止副本重新出现的记录。
同步调整本 PR 新增的“清空后保留 capture”测试,验证全量清空后旧历史可重新扫描、同一会话的新回合可继续采集、重启后行为一致,以及单条删除仍不会让副本重新召回。
| @@ -156,7 +157,6 @@ export class L3WorldModelTraceFieldPipeline { | |||
| : undefined; | |||
| if ( | |||
There was a problem hiding this comment.
升级后,包含用户反馈的旧 L3 演化任务会永久失败
问题
v9 数据库中尚未完成的项目 L3 演化任务,如果包含用户反馈,升级并建立旧项目到设备项目的 alias 后,再执行该任务会抛出终止错误 feedback scope mismatch,无法继续演化。
临时 v9 数据库的补充测试包含有效的 session、原始回合、L1、反馈和原始 batch 哈希:base 能调用模型并完成,本 PR 在调用模型前失败。
原因
本 PR 的设备级项目编号兼容没有覆盖完整读取链路:getBatch() 与 getSession() 已把历史项目编号转换为设备级项目编号,但 getFeedback() 经 feedbackFromSql() 读取时仍返回数据库中原始的 project_id。
这里直接比较反馈与 batch 的项目编号,将同一项目的新旧编号误判为不同项目,并抛出不可重试的错误。本 PR 的旧 batch 测试使用空 feedbackIds,因此没有覆盖这条兼容路径。
建议做法
在反馈仓储的读取边界统一应用与 session、batch 相同的项目 alias 规则,覆盖 getFeedback()、listFeedback() 及相关演化读取;继续保留数据库里的历史 UID、项目编号和旧 batch 的哈希输入。
增加含显式反馈的 v9 排队/失败任务升级后继续执行的测试,同时验证真正跨项目的反馈仍被拒绝、旧 batch 被篡改时仍不能通过校验。
| if (scope.memoryId && !existing) throw new Error(`corrupt L3 World Model scope memory: ${scope.memoryId}`); | ||
| if (existing) validateL3WorldModelMemory(existing, input.userId, projectId); | ||
| const fields = existing ? fieldsFromL3WorldModelMemory(existing) : emptyL3WorldModelFields(); | ||
| const fields = this.fields(null, projectId); |
There was a problem hiding this comment.
升级后首次更新项目约定,会让已应用的环境画像退出上下文
问题
这里的“扫描”是项目环境扫描:读取本地项目的目录结构、项目配置和相关运行时信息,用于生成或更新 L3 项目环境画像。它与 agent-source 扫描 Codex、Claude 等历史对话的功能不同。画像对应的“已应用扫描编号”记录这份画像由哪次项目环境扫描正式写入。
当升级前已有完成项目环境扫描并应用的项目环境画像、升级后还没有设备级 L3 记录时,首次更新“项目约定”或其他非环境画像字段,会让 Agent 读取的 projectEnvironmentProfile 从原来的内容变成 null。
补充测试先确认上下文能读到 Uses Node 22,再更新项目约定;base 更新前后都可读取,本 PR 更新后画像被过滤。画像正文仍在数据库中,并非被物理删除。
原因
这里通过 this.fields(null, projectId) 合并历史账号的 L3 字段,但后面读取已应用扫描编号 project_environment_applied_scan_id 时只看当前设备级 existing 记录。首次写入时该记录尚不存在,于是新 L3 记录继承了画像正文,却没有继承对应扫描编号。
随后 getMemory() 优先选择新记录,L3WorldModelContextReadModel 因缺少扫描编号而无法确认画像已应用,将其从返回上下文中去掉。
建议做法
合并历史 L3 字段时,一并携带该字段所属记录的有效元数据;继承环境画像时应保留与该画像对应的已应用扫描编号,后续更新其他字段也应保留它。仍需遵守删除/覆盖规则,避免从其他历史副本取来无关的扫描编号。
回归验证应直接检查实际 L3 上下文:旧库升级后更新项目约定或领域知识,环境画像保持可读,并覆盖重启、切换账号以及显式删除画像的场景。
问题
记忆虽存储在本机,但 UID 同时参与 namespace、项目身份、读取过滤、采集幂等和 L3 scope。同一设备切换账号后会看到不同记忆集合,历史副本也可能重复召回。
修改方案
主要代码位于 Memory/src/storage/(schema、设备身份与仓储兼容)、Memory/src/service/namespace/(UID 无关的 workspace 身份)、Memory/src/service/retrieval/(同源去重及三个召回入口)、Memory/src/service/evolution/ 和 read-model/(L3 历史兼容),以及 App backend、Agent memory 配置同步处。
验收
重点审核 schema v10 的历史值保留、项目 alias 的证据判定、L3 删除/旧 batch 兼容,以及三个召回入口是否统一使用同源身份规则。