在 multiroot workspace 中 qoder 的 历史对话功能无法使用

Issue Description

multi-root 工作区.code-workspace 含多个 folder)下,聊天侧栏的「对话历史」面板永久显示「暂无对话历史」,而该工作区的历史会话全部完好地存在本地数据库中。重启 Desktop App 无效。

根因并非数据丢失,而是 Qoder 对 multi-root 工作区的"项目标识"解析不稳定:写入会话记录时使用的标识与之后读取历史时使用的标识不一致;历史查询对 project_uri 做的是单值精确等值匹配,标识一旦漂移即返回 0 行。用户视角上等同于历史被清空,且无任何提示或报错。

Steps to Reproduce

  1. 在 SSH Remote 主机上创建一个 multi-root 工作区,例如:
// /data/code/my-ws.code-workspace
{ "folders": [
    { "path": "project-a" },
    { "path": "project-b" },
    { "path": "project-c" },
    { "path": "../../other-mount/group/project-d" },  // ← 跳出其余 folder 的公共父目录
    { "path": "project-e" }
] }
  1. 用该工作区打开窗口,创建并进行若干个对话(此时会正常写入 DB)。
  2. 完全退出 Qoder Desktop App,重新启动,重新打开同一个工作区(工作区文件内容不做任何修改)。
  3. 打开聊天侧栏的「对话历史」面板。
  4. 再次重启 App,重复第 4 步。

Expected Behavior

同一个 multi-root 工作区在重启前后应解析出稳定一致的项目标识;「对话历史」应列出该工作区的全部历史会话(本例应为 27 条)。即使标识发生变更,也应能兼容旧记录或自动迁移,而不应让已有历史变为不可见。

Actual Behavior

面板显示「暂无对话历史」,此前所有会话均不可见;重启无效。以下是排查得到的证据链。

1. 数据完好,按官方 SQL 复现可命中 27 条

aicoding-agent 的 agent 二进制中提取到历史列表查询原文:

SELECT session_id, user_id, user_name, session_title, project_id, project_uri,
       project_name, gmt_create, gmt_modified, org_id, session_type, mode,
       version, preferred_model_info
FROM chat_session
WHERE mode != 'agent_sub'
  AND session_type NOT IN ('inline','quest','voice','experts_add_user_message')
  AND user_id = ? AND org_id = ? AND project_uri = ?
ORDER BY gmt_modified DESC

以只读方式对 SharedClientCache/cache/db/local.db 复现(project_uri 传入工作区文件路径):

chat_session 总行数:108
命中行数(完整过滤条件 + project_uri = '/data/code/my-ws.code-workspace'):27

user_id / org_id 全库唯一,不存在账号或组织身份切换问题。库中 project_uri 仅 5 个取值,全为远端绝对路径:

project_uri 过滤后条数
/data/code/my-ws.code-workspace(multi-root 工作区文件) 27
/data/code/project-a(单独打开该 folder 时创建) 16
/home/<user>/dotfile 16
/other-mount 5
/home/<user> 1

结论:只要 project_uri 传对,面板就应显示 27 条。

2. 关键证据:同一个会话的产物在重启前后分裂到两个"项目"目录

~/.qoder/cache/projects/ 下,同一个 session id 的产物落进了两个不同的项目目录:

13:58 (重启前)  .qoder/cache/projects/my-ws.code-workspace-96cc20c4/agent-tools/<session-id>
14:00 (重启后)  .qoder/cache/projects/project-a-e45386c6/conversation-history/<session-id>
14:36 (重启后)  .qoder/cache/projects/project-a-e45386c6/agent-tools/<session-id>

期间 .code-workspace 文件内容未做任何修改。同一个 multi-root 工作区被解析成了两个不同标识:

  • 重启前 → 工作区文件路径my-ws.code-workspace
  • 重启后 → 第一个根 folderproject-a

而那 27 条历史的 project_uri 全部是重启前那套标识。读取侧使用漂移后的标识 → 等值匹配失败 → 空列表。

3. agent 内部并存多套 multi-root 标识策略

agent 二进制中可见以下符号,印证"项目标识"来源不唯一:

  • WorkspaceFoldersInitializeParams { WorkspacePath + WorkspaceFolders } —— 初始化同时传单一路径与多根列表
  • MergeBrokerProjectsFromWorkspaceFolders —— 多根需合并为"项目"
  • fallback_to_workspace_file_directory —— 回退到工作区文件所在目录(本例即 /data/code,库中 0 行)
  • preserve_client_workspace_foldersPrecheck failed: too many workspacesSuccessfully migrated workspace path
  • [quest][search] called with empty workspaceFolders

4. 已排除的可能

  • 不是客户端读了本地空库aicoding-agentextensionKind["workspace"],agent 运行在远端,读写同一个 DB。
  • 不是过期清理:清理逻辑为 6 个月保留(... < date('now','-6 months')),全部数据均为近几日。
  • 不是身份问题:全库 user_id / org_id 唯一。
  • 不是"最大活跃会话数"自动关闭导致删除:该机制只置状态,记录仍在库中。

5. 影响

严重度高:用户视角等同于历史会话全部丢失,重启不可恢复,无任何提示。所有使用 multi-root 工作区的用户都可能受影响。同一份代码以「单文件夹」与「multi-root」两种方式打开时,历史也会被切分为互不可见的两组。

6. 修复建议

  1. 统一项目标识的单一来源:multi-root 固定使用 .code-workspace 文件路径(或稳定 workspace ID),写入与读取共用同一解析函数。
  2. 读取侧放宽匹配:project_uri IN (工作区文件路径, 各 root 路径…)
  3. 持久化 workspace 身份,不依赖运行时路径推导。
  4. 检测到旧标识记录时自动归一(可复用已有的 Successfully migrated workspace path 逻辑)。
  5. 可观测性:历史为空时把所用 project_uri 打进日志;空态文案区分"无历史"与"查询无匹配"。

Screenshots / Screen Recordings

请在此处插入你已有的两张截图:

  1. 「对话历史」面板显示「暂无对话历史」(首次发现时)
  2. 重启 Desktop App 之后,同一面板仍显示「暂无对话历史」

如便于录屏,建议补一段:打开 multi-root 工作区 → 打开对话历史(空)→ 改用单文件夹方式打开同一 folder → 打开对话历史(有记录),对比更直观。

Operating System

  • 客户端
版本: 1.20.1
VSCode 版本: 1.106.3
提交: 4ee322f78fe8606b0bcb6dd6991c61463b3112de
日期: 2026-07-29T03:19:31.841Z
Electron: 42.2.0
Chromium: 148.0.7778.97
Node.js: 24.15.0
V8: 14.8.178.14-electron.0
OS: Darwin arm64 25.5.0
  • 远端(SSH Remote 目标机):Alibaba Cloud Linux 3 (OpenAnolis Edition),kernel 5.10.134-19.3.1.al8.x86_64,x86_64
  • 使用形态:Qoder Desktop App + SSH Remote + multi-root 工作区(5 个 folder,其中 1 个用 ../../ 跳出公共父目录)

Current Qoder Version (Menu → About Qoder → Copy)

<请从 Menu → About Qoder → Copy 粘贴客户端版本信息到此处>

远端 qoder-server 侧(从 ~/.qoder-server/bin/<commit>/product.json 读取):

name    : Qoder
version : 1.106.3
commit  : 4ee322f78fe8606b0bcb6dd6991c61463b3112de
quality : stable
date    : 2026-07-29T03:27:13.839Z