Skip to content

fix(client): session jump must navigate through uiWorkspace, not the removed sessions.open - #37

Merged
cloader merged 1 commit into
cloader:mainfrom
Fishsb:fix/session-jump-uiworkspace
Sep 26, 2026
Merged

cloader merged 1 commit into
cloader:mainfrom
Fishsb:fix/session-jump-uiworkspace

Conversation

@Fishsb

@Fishsb Fishsb commented Sep 26, 2026

Copy link
Copy Markdown
Contributor

Problem

On every dsh runtime since 0.1.6 (including the current 0.1.7-rc.2), the 🤖 跳转会话 ↗ button and the session-jump affordances on cards, chips, and execution rows do nothing. The click reports:

会话导航不可用,会话 ID:<id>

…even though the session is live and visible in the sidebar.

Root cause

src/client/session-jump.ts resolves the runtime sessions service and calls sessions.open(id). That selector no longer exists: the ISessions contract in 0.1.7-rc.2 states

Host catalog and local reference-source counts; navigation belongs to view owners.

Verified across releases:

@deepseek-ai/dsh-api-session-controller ISessions.open(id)
0.1.5-rc.2 present — open(id: SessionId): void ("Select a session as current.")
0.1.6-alpha.2 removed
0.1.7-rc.1 removed
0.1.7-rc.2 removed

Reading a method the service no longer ships yields undefined, and calling it throws. The jumper's own try { … } catch { return 'unavailable' } then swallows the TypeError into a bare "navigation unavailable" verdict — so the removal degraded silently instead of failing loud.

The supported navigation entry is uiWorkspace.openSession(target) ("Select a Session and show its Conversation as one UI navigation action").

Fix

Resolve the navigation entry structurally instead of assuming a method exists:

const navigate = (sessions, sessionId) => {
  const nav = access.getUiWorkspace?.()
  if (typeof nav?.openSession === 'function') { nav.openSession(sessionId); return true }  // 0.1.6+
  if (typeof sessions.open === 'function')    { sessions.open(sessionId);    return true }  // 0.1.5 and older
  return false
}
// …
return navigate(sessions, sessionId) ? 'opened' : 'unavailable'
  • SessionsServiceFace.open is now optional, and getUiWorkspace is an optional accessor, so existing callers (and the existing tests that inject only two accessors) keep compiling. The legacy path is retained for older runtimes rather than dropped.
  • unavailable now means "this runtime provides no navigation entry" — a real verdict, not a swallowed exception.
  • Accessors stay lazy/per-click (unchanged rationale: a service provided after apply must not be captured as undefined).
  • lib/client.js rebuilt.

Verification

  • Red → green: the new regression test drives the jumper against a runtime-shaped sessions service that has no open key at all. Before the fix it fails with expected 'unavailable' to be 'opened'; after, it passes.
  • npm test: 352/352 pass (24 files). tsc --noEmit clean.
  • npm ci && npm run build && npm test (the CI pipeline) run on a clean clone: build reproduces the committed lib/client.js byte-for-byte, leaves the tree clean, tests green.
  • Committed bundle delta vs main proven to be exactly two edits — the navigate() helper and the third accessor at the call site — nothing incidental.
  • End-to-end against a live host: the bundle the running dsh web server actually serves was fetched over HTTP, the jumper function extracted, and exercised against both runtime shapes (0.1.7: no open, uiWorkspace present → opens; legacy: sessions.open only → opens) plus the archived/missing/unavailable verdicts.

Note for older runtimes

The legacy sessions.open branch is deliberately kept, so this fix is additive for 0.1.5-era hosts and is not a breaking change for the dshReleases compatibility matrix.

…ions.open

DSH 0.1.6 removed the 'sessions' service's open() selector — the ISessions
contract now reads 'navigation belongs to view owners', and the only supported
navigation entry is uiWorkspace.openSession(target). Every jump on a 0.1.7
runtime therefore read an undefined method and called it; the TypeError was
swallowed by the jumper's catch into a bare 'unavailable' verdict, so the
'🤖 跳转会话' button silently did nothing on every recent DSH.

- session-jump: resolve the navigation entry structurally — uiWorkspace first
  (0.1.6+), legacy sessions.open only while it actually exists, 'unavailable'
  only when neither is present. sessions.open is now declared optional.
- client entry: pass the uiWorkspace accessor (still resolved per click, so a
  late-provided service cannot be captured as undefined).
- tests: regression guard that drives the jumper against a runtime-shaped
  sessions service with no open() key and asserts it still opens.

Verified end to end: 352/352 suite, tsc clean, and the bundle the running GUI
actually serves was extracted from its HTTP response and exercised against
both runtime shapes (0.1.7 and legacy 0.1.5).
@cloader
cloader merged commit e485fc8 into cloader:main Sep 26, 2026
1254087415 pushed a commit to 1254087415/dsh-taskboard that referenced this pull request Sep 26, 2026
上游 8 个 commit(v0.8.1 → v0.8.3):
- cloader#33 执行开场注入改用看板自身的 source kind(适配 DSH 0.1.7-rc.2 的 v4 会话格式)
- cloader#35 定期任务完成策略(默认「完成后新建待办」)
- cloader#37 会话跳转改走 uiWorkspace.openSession(sessions.open 在 DSH 0.1.6+ 已移除)
- cloader#38 台账写入拒绝用旧快照覆盖新文件;cloader#39/cloader#40 结算与认领超时判定修正
- devDeps 升到 0.1.7-rc.2

冲突解决(仅 2 处源码,其余 lib/* 由 npm run build 覆盖):
- src/client/index.ts:保留本 fork 的 mountSessionCardLinks 接线,同时引入上游的 UiWorkspaceFace / getUiWorkspace
- src/shared/version.ts:采用上游 0.8.3

本地 5 项增强逐项核对保留:会话自动跟踪、会话导入、会话↔卡双向跳转、
侧边栏 4 数字统计、Agent 协议纪律 9/10。
测试:360/360 通过(本机 Node 26 需 NODE_OPTIONS=--localstorage-file 才不会被 Node 自带的
localStorage 全局遮住 jsdom 实现)。
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.

2 participants