Issue Description
在 multi-root 工作区(.code-workspace 含多个 folder)下,聊天侧栏的「对话历史」面板永久显示「暂无对话历史」,而该工作区的历史会话全部完好地存在本地数据库中。重启 Desktop App 无效。
根因并非数据丢失,而是 Qoder 对 multi-root 工作区的"项目标识"解析不稳定:写入会话记录时使用的标识与之后读取历史时使用的标识不一致;历史查询对 project_uri 做的是单值精确等值匹配,标识一旦漂移即返回 0 行。用户视角上等同于历史被清空,且无任何提示或报错。
Steps to Reproduce
- 在 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" }
] }
- 用该工作区打开窗口,创建并进行若干个对话(此时会正常写入 DB)。
- 完全退出 Qoder Desktop App,重新启动,重新打开同一个工作区(工作区文件内容不做任何修改)。
- 打开聊天侧栏的「对话历史」面板。
- 再次重启 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) - 重启后 → 第一个根 folder(
project-a)
而那 27 条历史的 project_uri 全部是重启前那套标识。读取侧使用漂移后的标识 → 等值匹配失败 → 空列表。
3. agent 内部并存多套 multi-root 标识策略
agent 二进制中可见以下符号,印证"项目标识"来源不唯一:
WorkspaceFoldersInitializeParams { WorkspacePath + WorkspaceFolders }—— 初始化同时传单一路径与多根列表MergeBrokerProjectsFromWorkspaceFolders—— 多根需合并为"项目"fallback_to_workspace_file_directory—— 回退到工作区文件所在目录(本例即/data/code,库中 0 行)preserve_client_workspace_folders、Precheck failed: too many workspaces、Successfully migrated workspace path[quest][search] called with empty workspaceFolders
4. 已排除的可能
- 不是客户端读了本地空库:
aicoding-agent的extensionKind为["workspace"],agent 运行在远端,读写同一个 DB。 - 不是过期清理:清理逻辑为 6 个月保留(
... < date('now','-6 months')),全部数据均为近几日。 - 不是身份问题:全库
user_id/org_id唯一。 - 不是"最大活跃会话数"自动关闭导致删除:该机制只置状态,记录仍在库中。
5. 影响
严重度高:用户视角等同于历史会话全部丢失,重启不可恢复,无任何提示。所有使用 multi-root 工作区的用户都可能受影响。同一份代码以「单文件夹」与「multi-root」两种方式打开时,历史也会被切分为互不可见的两组。
6. 修复建议
- 统一项目标识的单一来源:multi-root 固定使用
.code-workspace文件路径(或稳定 workspace ID),写入与读取共用同一解析函数。 - 读取侧放宽匹配:
project_uri IN (工作区文件路径, 各 root 路径…)。 - 持久化 workspace 身份,不依赖运行时路径推导。
- 检测到旧标识记录时自动归一(可复用已有的
Successfully migrated workspace path逻辑)。 - 可观测性:历史为空时把所用
project_uri打进日志;空态文案区分"无历史"与"查询无匹配"。
Screenshots / Screen Recordings
请在此处插入你已有的两张截图:
- 「对话历史」面板显示「暂无对话历史」(首次发现时)
- 重启 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