Skip to content

fix(v2): 修复子代理丢失、后台误判与多实例覆盖,新增费用统计与设置菜单 - #25

Merged
Hotakus merged 8 commits into
Hotakus:masterfrom
aiev:fix/multi-instance-merge
Sep 24, 2026
Merged

Hotakus merged 8 commits into
Hotakus:masterfrom
aiev:fix/multi-instance-merge

Conversation

@aiev

@aiev aiev commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

修复

  1. V2 历史扫描丢失子代理:V2 适配层把 tool part 转成 V1 形态时丢掉了 id,扫描对每个 part 都找不到 key(seen=N, booked=0),侧栏既不显示也不更新。→ 保留 part.id;扫描同时把终态条目的 sessionId / tokens / cost / model 回填到已有条目。
  2. 后台子代理被误判为已完成:part 级 metadata 为空壳({})时,扫描停在空壳上、读不到 state.metadata 里的子会话 ID,导致「后台任务保持运行」的判定失效——子代理还在跑,侧栏却已标成 done(运行时无 running 计数)。→ 新增 partMetadata 统一读取 part/state 两级并忽略空壳;适配层改为合并两级 metadata 后再双写 session_id/sessionId。
  3. 多开 TUI 互相覆盖:持久化原先每次写整表快照,后写入的实例会丢掉其他实例刚创建的条目。→ 新增 KVApi.update 原子读-改-写,并用合并规则(终态优先 + 字段并集,mergeSubEntries)落盘;宿主没有 update 时退回读-改-写。
  4. 假 running / 漏事件兜底:success 事件缺少子会话 metadata 时前台任务仍回落 completed;非后台 tool 已完成但条目仍停在 running 时轮询自动补扫;存在 running 条目时每约 30s 与历史对账一次。无子会话链接的条目不再按时间阈值猜测收尾——V2 通过本地数据库回填真实子会话链接与终态(见下)。
  5. model 字段脏数据:host 的 session.model 是对象({ providerID, id }),旧代码 String() 后写入 "[object Object]"。→ 统一 modelIdOf 提取 id;历史脏值会被重新读取修复,无法修复时不再显示。

新增

  • 标题栏总费用($0.42,2 位小数);/subagent-cost 可开关列表内条目费用(4 位小数,默认关;展开区始终显示)。
  • /subagent-time、/subagent-tokens 开关列表内耗时与 Token(默认开,行为与旧版一致)。
  • /subagent-config 设置摘要;/subagent-sections 原生循环设置菜单(V2)。
  • /subagent-time-format 耗时格式:short(默认)/ decimal / clock / compact / seconds。
  • 本地数据库补全(V2,默认开,/subagent-db 或设置菜单可关):只读读取宿主 opencode.db(可用 OPENCODE_DB 覆盖路径,遵循 XDG_DATA_HOME,bun:sqlite 动态加载):
    • 用父会话 assistant 消息中工具 part 的 state.metadata.sessionID,按 call id 回填前台子代理缺失的子会话链接(session.tool.success 事件流里 host 不带该字段);
    • 从 session_v2 读取子会话的 agent / model / tokens / cost,并以 time_idle / idle_outcome 作为真实终态(interrupted 视为取消);
    • 数据库不存在、bun:sqlite 不可用或结构不符时静默回退宿主 API,绝不影响面板其余功能。
  • 4 语言 i18n 补齐全部新设置项。

验证

  • npm run typecheck / npm run build / npm test:26 项测试全部通过。
  • 多 TUI 实例手工验证:条目不再互相覆盖;会话历史扫描可恢复漏事件的子代理。
  • 本地库验证:前台调用由库中 metadata.sessionID 回填(示例 call_00_ezBAm9vqjFCWRCjYpzeI2029 → ses_f2f98c74cffefP04Np4yGiL7PL),model / tokens 与 time_idle 终态一致。
  • CI 增加 npm test 步骤。

备注

  • 默认值保持旧版显示:列表耗时/Token 默认显示,费用默认只在展开区显示,耗时默认格式仍是 short(45.32s / 112m35s)。
  • 修复集中在 V2 适配层;V1 行为不变(/subagent-config 与 /subagent-sections 为 V2 专属,其余命令 V1 均有对应)。
  • 本地库读取属于宿主内部实现,仅作补充数据源:默认只读、可关闭,遇到不兼容结构自动回退;远程 serve 等没有本地数据库的环境同样回退宿主 API。
  • V1 无本地库补全,超出宿主消息窗口的旧条目仍无法回填费用。

- 保留 tool part id,修复历史扫描看不到子代理的问题
- 合并 part/state 两级 metadata,后台子代理不再被误判为已完成
- 非后台条目完成但未收到 success 事件时,发现轮询触发补扫
- KVApi.update 原子合并写入,多 TUI 实例不再互相覆盖;无 update 时退回读改写
- 扫描/轮询恢复漏事件的条目,周期校正 idle 僵尸条目
- 标题栏总费用(2 位小数);/subagent-cost 列出条目费用(4 位小数,默认关)
- /subagent-time、/subagent-tokens 开关耗时与 Token 显示(默认开)
- /subagent-config 设置摘要;/subagent-sections 原生循环设置菜单
- /subagent-time-format:short(默认)/ decimal / clock / compact / seconds
- i18n 四语言补齐新设置项
- 前台任务(无子会话 metadata)的工具完成事件不再被丢弃,条目即时收尾
- 后台任务缺少 metadata 仍交由扫描/轮询兜底
- 存在 running 条目时每约 30s 与历史对账一次
- host 的 session.model 为对象时提取 id,不再写入序列化垃圾
- 历史脏值视为缺失,scan/enrich 重新读取修复;无法修复时不再显示
- 只读读取宿主 opencode.db(bun:sqlite 动态加载,OPENCODE_DB/XDG_DATA_HOME 可定位)
- 用父会话工具 part 的 state.metadata.sessionID 按 call id 回填子会话链接
- 从 session_v2 读取 agent/model/tokens/cost 与 time_idle/idle_outcome 终态
- 移除无链接 running 条目的时间阈值猜测;扫描/对账/用量轮询均走真实数据
- 新增 /subagent-db 开关、设置菜单项、4 语言文案与 tests/db-index.test.ts
@Hotakus Hotakus added the bug Something isn't working label Sep 24, 2026
@Hotakus

Hotakus commented Sep 24, 2026

Copy link
Copy Markdown
Owner

感谢你的修复和新功能添加,但是在我本机实测发现一个问题,该问题可能导致子代理条目中的“模型”字段透传形如 {"id":"deepseek-flash",...} 的 json 格式数据,该问题貌似为该pr引入,1.6.1主线版本实测模型字段显示貌似正常。

286a84aa-9591-4018-bd06-87772c25fbad

- session_v2.model 是序列化 JSON 文本;modelIdOf 现在解析并取 id
- 适配层改用共享的 modelIdOf(去掉本地副本)
- validModel 拒绝以 "{" 开头的脏值,防旧 KV 数据回显
- 新增 JSON 形态的单元测试
@aiev

aiev commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

感谢反馈!确认是本 PR 的本地库补充路径引入的问题,已在 4f1d4df 修复并推到分支。

原因:session_v2.model 这一列存的是序列化后的 JSON 文本({"id":"deepseek-flash","providerID":"deepseek",...})。当子代理还没有带 model 的 assistant 消息时(刚创建 / 正在运行),代码把这个 JSON 文本当成了模型名显示;而本地库读取优先于宿主 API,于是覆盖了 1.6.1 原本正常的显示。

修复:

  • modelIdOf 现在识别以 { 开头的字符串,JSON.parse 后取 id(对象与普通字符串形态保持兼容);
  • V2 适配层统一使用同一实现,去掉本地副本;
  • 面板的 validModel 会过滤这类 JSON 脏值(包括已写入 KV 的历史值,显示上不再回显);
  • 新增对应单元测试。

验证:npm run typecheck / npm run build / npm test(27 项)全部通过。

麻烦再试一下;如果还有异常,截图给我,我继续跟进。

@Hotakus

Hotakus commented Sep 24, 2026

Copy link
Copy Markdown
Owner

感谢你的修复,代码合并后,我会尽快发布新版本,感谢你的支持

@Hotakus
Hotakus merged commit ac87a0e into Hotakus:master Sep 24, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants