Skip to content

fix: resolve current session via public read face (issue #64) - #68

Merged
taekchef merged 3 commits into
omdsh-dev:mainfrom
pinzza:fix/64-current-session-resolution
Sep 30, 2026
Merged

taekchef merged 3 commits into
omdsh-dev:mainfrom
pinzza:fix/64-current-session-resolution

Conversation

@pinzza

@pinzza pinzza commented Sep 24, 2026 •

Copy link
Copy Markdown

Fixes #64.

根因确认(与 issue 分析一致)

DSH 0.1.6-alpha.2(内核 commit 6830e1460d)移除了 SessionListState.current。插件 1.4.11-preview.1 有 9 处直读 sessions.list.getSnapshot().current,在新宿主上恒为 undefined;attachAndSend() 在进入 try/catch 之前就 return false,批注块从不进草稿且无任何提示。submitAttached / watchInputDraft / 按会话持久化 / 会话切换检测同因失效。

已发布宿主实测:current 在 0.1.6-alpha.2 ~ 0.1.7-rc.2 上都没有恢复过(issue #64 已有 4 位用户独立复现)。

修订说明(2026-09-28,回应本 PR 评论)

初版正文声称 ① sessions.currentProvideInfo 是"新旧宿主均有"的公开读面。这条是错的,由 @WhiteLNK @george-wyy 核出、我也独立复核确认:

  • 该读面只存在于旧源码线(packages/client/runtime/src/client/contract/sessions.ts;本地 clone 停在 2026-08-19 / 0.1.0-rc.8);
  • 当前发布宿主(0.1.7-rc.2)整棵安装树检索 currentProvideInfo → 0 命中;dsh-client-runtime 未随包安装;
  • 上游 master 现契约在 packages/api/session-controller/src/client/contract/sessions.ts,ISessions 成员无 currentProvideInfo;
  • 上游 master code search:currentProvide → 0;对照词 retainInfo → 73、SessionListState → 60、list.getSnapshot → 25(搜索有效,非索引缺失)。

⇒ 该级在所有已知宿主上都是死代码,本 PR 已将其整条删除;相应地把"为什么不用 localStorage"那段论据一并改正——在当前发布版上它就是唯一生效路径。

改动

client.js

  • 新增 currentSessionId() 三级解析(apply() 内,逐级 try/catch 降级;localStorage 缺席时如 Node 单测环境直接跳过该级):
    1. localStorage['dsh.sessions.current'].sessionId —— 内核 workspace 持久化的选择态(createSnapshotStore({}, { persist: { name: 'dsh.sessions.current' } }),写入形状 { sessionId },会话归档时由内核 clearArchivedCurrent 清理)。0.1.6-alpha.2 ~ 0.1.7-rc.2 上这是唯一真实可用的路径;
    2. list 快照的 current —— ≤ 0.1.6-alpha.1 的宿主,该字段是精确选中值;
    3. list 行上的 retainedBy.mainView > 0 —— 公开成员,兜底"尚未落盘 / 存储被清"。它是主视图 retain 计数而非选中值,内核自己(dsh-client-ui-session/lib/client.js:283)也只在 id 未知时才用它,故排在精确值之后;
  • 9 处直读全部改为经 helper(含 writeCurrentPendingQuotes / 2 处选区过滤 / attachAndSend / submitAttached / watchInputDraft ×2 / 会话恢复 ×2);
  • 会话切换检测:保留 list.subscribe(旧宿主)+ 1s 轮询兜底(新宿主选择态已移出 list store,没有可订阅的选中态来源);清理函数统一释放订阅与轮询;
  • 静默失败改为可观测:attachAndSend 取不到 id/scope 时 console.warn + 新增 toast.noSession(zh/en)提示,批注保留待下一条重试;submitAttached 同理加 warn;选区侧栏过滤在 id 未知时放行而非一律拦截。

测试

  • message-block.test.mjs:夹具支持注入 localStorage;新增"新内核形状 + 持久化选择态"与"无持久化键时退回 retainedBy.mainView"两组回归(zh/en 各一),保留旧内核形状用例;
  • pending-clear.test.mjs:会话切换断言改为单订阅 + 轮询 + 清理;解析优先级断言改为 localStorage → list.current → retainedBy,并新增"不允许残留直读""不允许再读 currentProvideInfo"防退化断言。

验证

  • node --check client.js 通过;
  • npm test:28/28 通过(原 26 + 新增 2);
  • 本机端到端:同一套改动移植到已发布 1.4.10(该版本 7 处直读)后,在 0.1.7-rc.1 与 0.1.7-rc.2 上批注均随消息正常送达(Annotation N:… 协议块与回复侧芯片正常);本机生效路径确认为第 1 级 localStorage,与 @WhiteLNK 的独立实测一致;
  • 全程未动 src/,tsc 构建不受影响(本机未装 devDeps,未跑 npm run check 全链,CI 为准)。

未并入本 PR(范围待维护者定)

@george-wyy 报告的"失效期间已写批注刷新即永久丢失"(dsh.annotation.pending.v1.* 从不落盘)是同一根因的第二个后果。本 PR 修好 id 解析后,正常路径不再触发;仅当三级解析全部失败(此时会 warn + toast,不再静默)时仍存在该风险。是否把"无会话时的批注暂存"做成显式降级路径,建议单开 issue 讨论,以免本 PR 继续变大——维护者若希望一并修,我并进来。

DSH 0.1.6-alpha.2 (kernel 6830e1460d) removed sessions.list.current,
so attachAndSend/submitAttached/watchInputDraft/pending-restore all
silently missed: current === undefined hit an early return before the
try/catch, with no toast or log.

Introduce a 3-tier currentSessionId() helper inside apply():
1) public sessions.currentProvideInfo.getSnapshot().sessionId
   (ISessions stable read face, present on old and new hosts,
   subscribable on session switch — the normal path);
2) legacy sessions.list.getSnapshot().current (pre-0.1.6 hosts
   and old test doubles);
3) private persistence key dsh.sessions.current (last resort only;
   normal path never reaches it).

Session-switch detection subscribes to both list and
currentProvideInfo plus a 1s poller (list.subscribe no longer fires
on switch since the selection moved out of the list store).
Missing-session failures now warn + toast (new toast.noSession)
instead of silent return; sidebar source filtering is permissive
when the id is truly unknown.

Tests: harness injects the helper and mocks provideInfo; add
new-kernel (no list.current) and legacy (list.current only)
regression cases plus a resolver-order guard.
@WhiteLNK

WhiteLNK commented Sep 25, 2026 •

Copy link
Copy Markdown

在已发布的 0.1.7-rc.2 上复核:第 ① 级 sessions.currentProvideInfo 不存在;建议把正文的版本范围收紧

先说结论:本 PR 的三级降级设计我认为是对的、值得合并。但正文里"① sessions.currentProvideInfo …… 新旧宿主均有"这一句,与我在最新发布版上的核对结果不一致,建议把表述对齐事实。

复核方法与结果

  1. 本机安装树(DSH 0.1.7-rc.2;@deepseek-ai/* 的安装根与 profile 树两处):检索 [Pp]rovideInfo(.js / .d.ts / .md 全覆盖)⇒ 0 命中;

  2. ISessions 公开面逐行读完(@deepseek-ai/dsh-api-session-controller/lib/types/client/contract/sessions.d.ts,全文 155 行),成员为:

    list · retain · using · retainInfo · searchResultLimit · create · subagentAddress · refreshProjections · refresh · search · fork · scope · scopeOf · sessionOf · binding

    —— 没有 currentProvideInfo;

  3. 0.1.7-rc.2 就是当前最新发布版(npm dist-tags:next = 0.1.7-rc.2、alpha = 0.1.7-alpha.2、latest 仍停在 0.1.5 系)⇒ 在所有当前已发布的宿主上,第 ① 级都不存在;

  4. 顺带一条:我按本 PR 引的路径 packages/client/runtime/src/client/contract/sessions.ts 在上游仓库取不到该文件;而实际承载 ISessions 契约的 packages/api/session-controller/src/client/contract/sessions.ts(7,660 字符)里也没有 currentProvideInfo。如果你是在更新的源码 / 未发布构建上验证到它存在,那正是需要写进正文的版本范围。

影响:在已发布宿主上第 ① 级恒取不到 ⇒ 实际路径变成 ②(list.current,0.1.6-alpha.2 起已移除)→ ③(localStorage)。也就是说,"为什么不用 localStorage"这条论据在当前发布版上恰好反过来成立——兜底键就是正常路径。三级降级本身没错,只是措辞需要对齐事实。

建议改法

① sessions.currentProvideInfo.getSnapshot().sessionId —— 在提供该成员的宿主上优先(我在 <你验证过的版本> 上确认存在);在 0.1.7-rc.2 及更早的已发布宿主上不可用,此时依次落到 ② / ③。

另一个数据点(我已据此调整了自己的临时补丁)

我先用过一条不依赖私有键的路径:sessions.list.getSnapshot().byId 行上的 retainedBy.mainView > 0(官方适配器 @deepseek-ai/dsh-client-ui-session/lib/client.js:281-283 正是这么取"主视图保留的会话"),7 处直读全替换后硬刷新即生效、无需重启。但它取的是"主视图保留的会话",多会话 / 多保留视图下未必等于用户当前选中 ⇒ 我不主张用它当第 ① 级。随后我改成与本 issue 建议一致:localStorage['dsh.sessions.current'].sessionId 优先、retainedBy 仅兜底、snap.current 留给旧宿主。所以第 ③ 级的选择我认同——只是希望正文把"公开读面是否存在"的版本范围写准。

一个已验证的实现细节(对 review 或许有用)

宿主 @deepseek-ai/dsh-client-modules 有 HMR 钩子(lib/index.js:600-625 的 rebuilt() 按 mtime/ctime/size 比对 → readFileSync → 重新 compose → 通知重建)⇒ 改完插件的 client.js 后硬刷新(Ctrl+Shift+R)即可生效,不必重启 dsh web(与 #64 里"硬刷新后 Console 出现日志"的观察一致)。

追加(同日实测,我自己的临时补丁)

我自己在 0.1.7-rc.2 上的临时补丁已改成与本 issue 建议同构的三级解析(① localStorage['dsh.sessions.current'].sessionId → ② snap.current → ③ byId[].retainedBy.mainView > 0),端到端通过:批注随消息正常送达(Annotation 1:… 协议块与回复侧芯片均正常),且 dsh web 重启后复测仍通过。⇒ 第 ③ 级(localStorage)在 0.1.7-rc.2 上是可用的兜底路径,本 PR 的降级设计在该宿主上成立。


— DeepSeek-V4.1 Flash(AI Agent;上述核对与实测由我在 WhiteLNK 的机器上完成,经其 GitHub 账号发布)

@george-wyy

Copy link
Copy Markdown

补充一条在已发布版本上的核对结果,可能与 #68 的主路径有关。

环境(与 #64 一致,独立复现)

  • 宿主:DSH 0.1.7-rc.2(npm next 标签,2026-09-24 发布;不是源码 checkout)
  • 插件:@changfenhuang/dsh-annotation@1.4.10
  • 现象:选区、高亮、编号脚标、「批注 ×N」全部正常;回车后批注块不进消息,Console 无 [annotation] 批注块已拼入草稿…,无 toast、无报错。

一个额外的可观测后果:不止"发不出去",而是"刷新即永久丢失"

因为 writePendingQuotes() 用的是同一个 id,localStorage 里从头到尾不会出现 dsh.annotation.pending.v1.*(我这边实测:整个 localStorage 只有 7 个键,没有任何批注键)。结果是已写好的批注在刷新/切会话后无痕消失——我这边因此丢了 6 条。这条可以和 #64 的失效清单互相印证。

关于 #68 的主路径:currentProvideInfo 在 0.1.7-rc.2 上取不到

在 0.1.7-rc.2 的安装树里核对:

  • @deepseek-ai/dsh-api-session-controller@0.1.7-rc.2 就是提供 ctx.sessions 的包(lib/client.js:rootCtx.reflect.provide("sessions", this, void 0));
  • 它的 ISessions(lib/types/client/contract/sessions.d.ts)成员里没有 currentProvideInfo;
  • 对该版本整个 node_modules/@deepseek-ai 全文检索,currentProvideInfo / provideInfo 命中 0 处;
  • dsh-client-runtime 也没有随 0.1.7-rc.2 安装(npm 上该包存在,latest 0.0.1-rc.1)。

所以按 #68 的三级顺序,在 0.1.7-rc.2 上第 ① 级取不到 → 第 ② 级 list.current 已被移除 → 实际落到第 ③ 级 localStorage['dsh.sessions.current'],也就是该 PR 本想避开的私有键。

想确认一下:currentProvideInfo 是 0.1.7-rc.2 之后才加入的,还是需要额外挂 dsh-client-runtime?如果是前者,#68 在已经发布的 0.1.7-rc.2 上就只有兜底路径可用。

一个 0.1.7-rc.2 上确实存在的公开读面

内核自己的 @deepseek-ai/dsh-client-ui-session(publishMain())判断"当前会话"用的正是 retainedBy.mainView:

const byId = sessions.list.getSnapshot().byId;
const nextId = Object.values(byId).find(c => (c.retainedBy.mainView ?? 0) > 0)?.id;

SessionSummary.retainedBy 与 ISessions.retainInfo(id) 都是公开成员、在 0.1.7-rc.2 上都在,可以作为第 ① 级的候选:

function currentSessionId() {
  var st = sessions.list.getSnapshot()
  if (st.current !== undefined) return st.current        // <= 0.1.6-alpha.1
  var byId = st.byId || {}, ids = st.ids || Object.keys(byId)
  for (var i = 0; i < ids.length; i++) {
    var n = (byId[ids[i]] || {}).retainedBy
    if (n && typeof n.mainView === 'number' && n.mainView > 0) return ids[i]
  }
  return undefined
}

语义上它是"主视图 retain 的会话",与真正的 selection 在边角状态下可能有差异(主视图停留在归档会话、或右侧栏 retain 了另一个会话时),优先级请作者判断。

我按这个思路在本地给 1.4.10 打了 7 处替换的补丁(文件语法与加载路径已核对,端到端待宿主重新读取 bundle 后确认)。如果 #68 最终采纳别的读面,我这边跟着改即可——这里只是提供一个 0.1.7-rc.2 上当下可用的选项。

@Ztyss

Ztyss commented Sep 26, 2026

Copy link
Copy Markdown

在 DSH 0.1.7-rc.2(DSH Desktop 携带该内核,Web GUI)+ @changfenhuang/dsh-annotation 1.4.10 上复现了 #64 的完整症状:批注创建、高亮、编号脚标、「批注 ×N」标签全部正常,唯独发送时批注块不进草稿——模型只收到正文,console 无任何报错,与 attachAndSend() 在 current === undefined 处静默 return false 的表现一致。

换言之,#64 的结论在 rc.2 上继续成立(0.1.6-alpha.2 起移除 SessionListState.current,rc.1 / rc.2 均无此字段),目前没有任何一个发布版本覆盖 0.1.7 线。

求合并本 PR,并建议按仓库的版本线惯例发布一个面向 0.1.7-rc.2 的版本(类似 rewind 的 per-line 发布方式)。合并后我们可以在 rc.2 隔离实例上第一时间实测反馈。谢谢!

@pinzza

pinzza commented Sep 28, 2026

Copy link
Copy Markdown
Author

感谢三位的复核——结论我认了:第 ① 级在已发布宿主上取不到,PR 正文的版本范围写错了,而且比我原先以为的更彻底:它不是"还没发布",而是在当前源码线里已经被移除。

以下是我的自查结果,供 review 参考。

1. 我错在哪:拿了一份过期源码当"公开读面"

我诊断时读的源码树是本地 clone,HEAD 停在 2026-08-19 / 0.1.0-rc.8,里面确实有:

packages/client/runtime/src/client/contract/sessions.ts:30
  readonly currentProvideInfo: HostObservable<SessionMaybeProvideInfo>

(rename 提交 b7f3cd3d78,2026-07-28)我据此写了"新旧宿主均有",没有核对真正在跑插件的那棵树。@WhiteLNK @george-wyy 的核对是对的。

2. 已发布宿主 / 上游现状(复核数字)

  • 运行宿主:npm 全局 @deepseek-ai/dsh(我这边 09-24 当天是 0.1.7-rc.1,现在是 0.1.7-rc.2)。对整棵安装树 grep -rl currentProvideInfo → 0 命中(exit=1,全树扫完,不是超时);dsh-client-runtime 未随包安装,与 @george-wyy 的核对一致。
  • SessionListState(0.1.7-rc.2)= { ids, byId, phase, projectionsBySession },current 已移除。
  • 上游 master(deepseek-ai/deepseek-harness,公开仓库,09-27 推送)code search:currentProvide → 0;对照词 retainInfo → 73、SessionListState → 60、list.getSnapshot → 25(说明搜索本身有效、不是索引缺失)。契约文件已迁到 packages/api/session-controller/src/client/contract/sessions.ts(7790 B),成员为 list / retain / retainInfo / searchResultLimit / create / subagentAddress / refreshProjections / refresh / scope / scopeOf / sessionOf / binding —— 没有 currentProvideInfo。

⇒ 正文引用的 packages/client/runtime/... 路径在上游已不存在;"① 新旧宿主均有"与"若内核恢复等价读取面,② 级会自然接管"两条都不成立。第 ① 级在已知宿主上是死代码。

3. 一个可交叉验证的数据点:本地补丁确实修好过,走的是 localStorage

我在本机 1.4.10 上移植了同一套替换(已发布 1.4.10 是 7 处直读;PR base 的 main 是 9 处),跑在 0.1.7-rc.1 上:批注随消息正常送达(Annotation 1:… 协议块与回复侧均正常,端到端实测通过),现在 0.1.7-rc.2 上仍在用。

因为该宿主上 ① 取不到、② 已被移除,生效路径只能是第 ③ 级 localStorage['dsh.sessions.current'] —— @WhiteLNK @george-wyy 的判断得到独立验证。该键确实存在,且是内核自己写的:dsh-client-ui-workspace/lib/client.js 里

selection = createSnapshotStore({}, { persist: { name: "dsh.sessions.current" } })
// 赋值处: selection.set({ sessionId: reference.sessionId })

写入形状正是 helper 解析的 { sessionId }。

也就是说:PR 想避开的私有键,实际是当前唯一的生产路径;正文"为什么不用 localStorage"那段论据在当前发布版上是反的。@WhiteLNK 的排序(localStorage 优先、retainedBy 兜底)是对的,我按你的结论改。

4. 我打算怎么改(已同步修订正文,补丁随后推上来)

  1. 修正正文:版本范围、引用路径,删掉"新旧宿主均有"与"内核恢复读面即可接管"的推论;
  2. 三级重排(并删除 currentProvideInfo 整条):
    • ① localStorage['dsh.sessions.current'].sessionId —— 内核 workspace 的持久化选择态,也是当前唯一已验证生效的路径;
    • ② list.getSnapshot().byId 行上的 retainedBy.mainView > 0 —— 公开成员,补"尚未落盘 / 存储被清"的场景。我原先想把它提为第 ①,现在收回:内核自己那段代码说明它是修复路径而非权威 ——
      // dsh-client-ui-session/lib/client.js:283(0.1.7-rc.2)
      const nextId = currentId !== void 0
        && (this.sessions.retainInfo(currentId).getSnapshot().retainedBy.mainView ?? 0) > 0
          ? currentId                                                   // 已知 → 先信它
          : Object.values(byId).find(c => (c.retainedBy.mainView ?? 0) > 0)?.id  // 未知 → 才用 mainView 猜
      所以它只适合当兜底,这一点与 issue [Bug] 批注块不随消息发送:DSH 0.1.6-alpha.2 移除了 sessions.list.current(1.4.10 / 1.4.11-preview.1 均受影响) #64 的建议一致;
    • ③ list.current —— 只服务 ≤ 0.1.6-alpha.1 的老宿主;
  3. 会话切换检测同步改:①② 都可订阅或轮询,保留 1s 轮询兜底(0.1.7 线上选择态迁出 list store,list.subscribe 已不再触发);
  4. @george-wyy 报的"刷新即永久丢失"(dsh.annotation.pending.v1.* 从不落盘)我认为应该修,但默认单开 issue、不并入本 PR,以免本 PR 继续变大;维护者若希望一起修,我并进来。

5. 现状同步

@changfenhuang main 最后一次提交 2026-09-15,本 PR 自 09-24 起无 review;#64 已有 4 位用户独立复现,0.1.6-alpha.2 ~ 0.1.7-rc.2 全部受影响(current 在这条线上没有恢复过)。方向若认可,我先把第 1–3 点的补丁推上来,方便在 rc.2 隔离实例上实测反馈。

…sh-dev#64)

Review follow-up for omdsh-dev#68. Drop the sessions.currentProvideInfo tier: it exists
only in the stale 2026-08-19 source line (packages/client/runtime), while the
current upstream contract (packages/api/session-controller) and every published
host (0.1.6-alpha.2 ~ 0.1.7-rc.2) have no such member — tree-wide search
returns 0 hits, and upstream master code search returns 0 for currentProvide
against 73/60/25 for retainInfo/SessionListState/list.getSnapshot.

Resolution order is now:
1) localStorage['dsh.sessions.current'].sessionId — the selection the kernel
   workspace persists (createSnapshotStore persist name, shape { sessionId },
   cleared by clearArchivedCurrent); the only path that works on published hosts;
2) list snapshot `current` — the exact selection on <= 0.1.6-alpha.1 hosts;
3) SessionSummary.retainedBy.mainView > 0 — public member, fallback only: it is
   main-view retention, not the selection, and the kernel itself
   (dsh-client-ui-session:283) uses it only as a repair path.

Session-switch detection keeps list.subscribe plus the 1s poll; the
currentProvideInfo subscription is gone with the tier.

Tests: the message-block fixture can inject localStorage and covers the
persisted-selection and retainedBy-fallback shapes; pending-clear pins the new
resolution order and forbids any currentProvideInfo usage. npm test 28/28.
@pinzza

pinzza commented Sep 28, 2026

Copy link
Copy Markdown
Author

已推送 v2:7a9f958c(PR 正文同步修订)。

一处与上一条评论的差异,先说明:上一条我把顺序写成 ① localStorage → ② retainedBy → ③ list.current;实现里把 ②③ 对调了,理由是 list.current 在老宿主上是精确选中值,而 retainedBy.mainView 是"主视图 retain 计数"(内核自己 dsh-client-ui-session:283 也只在 id 未知时才拿它猜)。所以最终顺序是:

  1. localStorage['dsh.sessions.current'].sessionId —— 内核 workspace 持久化的选择态,当前发布宿主唯一可用路径;
  2. list 快照的 current —— ≤ 0.1.6-alpha.1 宿主上的精确值;
  3. SessionSummary.retainedBy.mainView > 0 —— 公开成员,仅兜底"尚未落盘 / 存储被清"。

其余改动:

  • 删除 currentProvideInfo 整条(含会话切换里的订阅);切换检测保留 list.subscribe + 1s 轮询;
  • 测试:npm test 28/28(原 26 + 新增"无持久化键时退回 retainedBy" zh/en 两组),并新增"禁止再读 currentProvideInfo"防退化断言;
  • 本机端到端:同一套改动移植到已发布 1.4.10 后,在 0.1.7-rc.2 上批注随消息正常送达,生效路径确认为第 1 级(localStorage['dsh.sessions.current'] 有值),与 @WhiteLNK 的独立实测一致。

@WhiteLNK 你的排序建议已采纳,retainedBy 只作兜底;@george-wyy 提的"刷新即丢失"我建议按正文末节单开 issue,若你希望并进本 PR 我就并。@changfenhuang 麻烦看下这版还需要调整什么。

@Ztyss

Ztyss commented Sep 29, 2026

Copy link
Copy Markdown

v2 收到,降级顺序的修正很扎实(list.current 是精确选中值、retainedBy 只是猜测,这个对调理由成立)。mergeable 已通过,三个文件 +170/−23 也在易审的量级。

想追一下合并 + 发版的时间预期:npm 上最新还是 1.4.10(2026-09-11 发布),整条 0.1.7 内核线上现在没有任何已发布版本可用——#64(批注不随消息发出)和 #69(面板半透明)两个 0.1.7 回归在发布版上同时活着,批注功能对我们是每天都会撞的硬伤。

两个请求:

  1. 如果 review 没有别的悬念,能否尽快合入?
  2. 发版节奏上,即使来不及走完整 release,一个 1.4.11-preview(或 preview dist-tag)也行——我们在 0.1.7-rc.2 实机上可以小时内实测回帖(发送链路 + 半透明面板一起验),帮你把正式版的验证成本也省掉。

如果需要我再补 rc.2 上的任何复现材料,直接说。

Merges the uiSession.adapter.current read face (omdsh-dev#65) with the
localStorage / retainedBy fallback chain and the noSession toast +
pending-retry UX (omdsh-dev#68). Session-switch detection now layers the
uiSession source subscription, the legacy list subscription, and a 1s
poll. Verified against dsh-v0.2.0-rc.2 kernel sources; npm run check
passes 33/33.
@taekchef

Copy link
Copy Markdown
Collaborator

感谢 @pinzza 的考证——特别是 localStorage dsh.sessions.current 持久化键的内核证据链(createSnapshotStore persist 写入形状、clearArchivedCurrent 清理时机)和 retainedBy.mainView 只作修复路径的定位,这些结论我都逐条核实过,全部成立。

情况说明:#65(0.1.7 的 uiSession 方案)已先合入 main,与本 PR 在 client.js 的会话解析区域正面冲突。为了不让两条修复线互相阻塞,维护者直接接手在本分支上做了合并改造(你的原提交完整保留,署名不变):

  • readCurrentSessionId 合成四级回退链:uiSession.adapter.current(0.1.7+,已对照 dsh-v0.2.0-rc.2 内核源码核实仍在 packages/client/ui-session/src/client/index.ts:312)→ 你的 localStorage dsh.sessions.current(0.1.6-alpha.2 ~ 0.1.6)→ 旧 list 快照 current(≤0.1.6-alpha.1)→ 你的 retainedBy.mainView > 0 兜底。
  • 保留了你全部的 UX 工作:noSession toast、批注保留待下一条重试、console 诊断、watchInputDraft 切换重挂修复。
  • 会话切换检测在你的「list.subscribe + 1s 轮询」之上叠加了 uiSession source 订阅(0.1.7+ 上精确触发,轮询降级为兜底)。
  • 测试合并后 33/33 全绿,新增了四级链顺序断言(test/pending-clear.test.mjs)。

CHANGELOG 已注明方案与考证来自你。接下来会以同样的方式接手 #67/#66/#63/#56,之后一起发版。

@taekchef
taekchef merged commit d2f504c into omdsh-dev:main Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 批注块不随消息发送:DSH 0.1.6-alpha.2 移除了 sessions.list.current(1.4.10 / 1.4.11-preview.1 均受影响)

5 participants