dsh-client-ui-branch-map:DSH 工作區對話分支圖

前言

DeepSeek Harness(DSH)的對話視圖通常以列表或畫布形式呈現。當對話輪次較多、分支較多時,列表難以一眼看清整體結構,畫布又沒有明確的分支語義。dsh-client-ui-branch-map 提供一張對話分支圖:把當前工作區的對話畫成一張圖,每條消息是一個節點,每個 Session 是節點上的分支引用,可以像 git 分支一樣從任意節點切出去。

這是什麼

dsh-client-ui-branch-map 是一個 DSH 客戶端視圖插件,由維護者 AnichHaveatry 維護。它的核心是把 DSH 工作區裏的對話渲染成一張分支圖:根節點代表 Workspace,消息節點代表一輪對話,Session 是指向某個節點的引用。歸檔的對話不會進入這張圖,子代理運行也不會進入。

核心功能

分支圖視圖

視圖包含四個基本元素:

  • 作用域:恰好一個 Workspace
  • 根節點:一個,代表 Workspace 本身
  • 消息節點:用戶發出的一條消息(一輪對話)
  • 邊:節點 → 在該會話裏緊隨其後的下一條消息
  • Session:不是節點,而是指向某個節點的引用,和 git 分支指向 commit 一樣

一個 Session 的引用停在它自己擁有的最後一條消息上;還沒有自己消息的 Session 指向它被切出來的那個節點。兩個 Session 停在同一個節點,讀起來就像兩個 git 分支指向同一個 commit。

行佈局與根節點

行按輪號上升排列。輪號小的一定在前,所以這始終是一個合法的拓撲序;兄弟分支落在同一行帶,分叉就讀成兩條並排的 lane。

根節點在樹中水平居中:根級會話向左右兩側展開,新增會話不會把所有根線堆在左邊。

不畫什麼

  • 子代理運行:子代理是代理自己派出去的工作,不是用戶與它的交互;子代理 Session、以及從子代理再分出來的 Session,都不進入圖,也不佔節點。過濾按血緣做:isOwnConversation 一路走到根,只要祖先是 origin: "subagent" 就整條排除。
  • 歸檔的對話:歸檔集由宿主單獨給出:useWorkspaces 快照的 archivedSessionIds。歸檔的 Session 仍然是它工作區的成員,所以不能靠成員表過濾,必須顯式減掉。減掉的是它的引用,以及只由它引入的節點。
  • 當前打開的那條會話是例外:它被歸檔時照樣畫。工具欄的 已隱藏 N 條歸檔 數的就是被減掉的其它分支。
  • 存儲細節:沒有事件序號、沒有 Session id、沒有加載/刷新控件、沒有子代理計數。

操作控件

控件 效果
+分支 on a message ctx.sessions.fork({ sessionId, atSeq }) — 打開一個新 Session,帶上從根到這條消息的整條路徑,然後切過去。始終顯示,不藏在 hover 裏
改寫 on a message 在消息之前分叉,然後把改寫後的文本作爲新分支的第一條 prompt 發出去;原分支不受影響。輪次起點在日誌第一條事件時沒有”之前”可切,這個按鈕不出現(從根開新分支即可)
a ref chip on a node 點它打開那條會話現在所在的位置(它的 tip)。這和”從這個節點開新分支”是兩個不同的問題,標籤也分開寫
從根開新分支 on the root 一個從工作區根開始的空 Session

兩件容易混淆的事,界面刻意分開:

  • 打開分支去它的 tip,因爲 ref 記錄的是這條線走到了哪裏;不會把你送到中間的某個節點。
  • 從節點開分支纔是”站在這個節點上”:新 Session 的上下文是從根到它的路徑,下一句話從這裏繼續。

atSeq 是包含式的事件切點。一輪自己的 turn/start seq 是包含的,所以下一輪 turn/start 之前的那條事件就是這一輪的末尾——因此切點不會落在輪中間,新 Session 繼承節點的所有祖先、不會多帶一點。

數據與行爲邊界

這個插件不讀私有數據、不改對話、不聯網、不落盤。

  • 讀:只用 conversation.view 的標準客戶端 props:useSessions、useWorkspaces(含它的 archivedSessionIds 歸檔集)、useProjection("turnOutline");以及 ctx.sessions.refreshProjections(id)(宿主語義:只讀投影,不啓動遷移)。
  • 寫:視圖自身不寫任何東西。+分支 / 改寫 是用戶點擊後才調用的 ctx.sessions.fork(...),和 DSH 自帶的分支按鈕同一語義;不點就什麼也不發生。
  • 會話歷史:從不 retain 其它 Session、從不打開別人的歷史;不讀 useChat,不讀子代理目錄。
  • 存儲:無。沒有 localStorage / sessionStorage / IndexedDB / cookie / 插件配置文件。
  • 文件:插件自身不讀寫任何路徑。宿主可能按自己的投影緩存策略緩存派生值,但不會改會話日誌。
  • 網絡:無。沒有 fetch、WebSocket、遙測或外部服務。
  • 宿主半邊:lib/index.js 的 apply 是空的。不註冊服務、投影、路由或工具。
  • 停用 / 卸載:Tab 消失;樣式表通過 effect 清理,locale 註冊隨 fiber 卸載;沒有需要清理的插件數據。
  • 遷移:refreshProjections 不啓動日誌遷移;視圖也不打開別的會話,所以不會觸發格式遷移寫盤。

唯一”碰數據”的地方是用戶主動點分支產生的 Session,以及 openSession 的界面導航——這兩件事和 DSH 自帶的同名操作完全一樣。

安裝與啓用

這個包本身就是一個 DSH bundle:聲明瞭 dsh.bundle.patch,自帶的 cordis.patch.yml 按包名插入一行,不寫任何絕對路徑。

# cordis.patch.yml,隨包發佈
- insert:
    - id: ui-branch-map
      name: dsh-client-ui-branch-map

從 GitHub 安裝(倉庫自帶 lib/,安裝時不需要構建):

dsh plugin --profile desktop add github:AnichHaveatry/dsh-client-ui-branch-map

從 tarball 安裝:

dsh plugin --profile desktop add .\dsh-client-ui-branch-map-0.3.0.tgz

從目錄安裝(開發時用,link: 是活鏈接):

dsh plugin --profile desktop add C:\path\to\dsh-client-ui-branch-map

裝好之後重啓 DSH並刷新頁面。兩個緩存要到重啓才失效:

  • dsh-client-modules 會按 Loader specifier 緩存每個包的 client bundle 路徑;
  • bundle 字節在激活時快照,改 lib/client.js 不會熱更新。

兼容性:peerDependencies 聲明 @deepseek-ai/dsh: ^0.2.0-rc.2。不兼容的 DSH 會跳過這個 bundle 並點名,而不是掛上一個壞掉的視圖。pnpm 安裝時出現 peer 依賴警告是預期的:peer 由 DSH 安裝提供,不在 profile 裏。

這個包和 dsh-client-ui-canvas 是兩個獨立包、兩個獨立 Tab(canvas 與 branch-map),可以並存;想只留一個就卸掉另一個。

適用場景與注意

適合需要清晰查看對話分支結構的場景:多輪對話、多次分支、需要快速定位某個分支的當前位置。

插件以當前 dsh 進程權限運行,安裝前應檢查源碼與許可證。插件本身不落盤、不聯網、不讀私有數據,只通過標準客戶端 props 和 ctx.sessions.fork 與宿主交互。

短結尾

dsh-client-ui-branch-map 提供了一張清晰的對話分支圖,讓 DSH 工作區的分支結構一目瞭然。它像 git 一樣,用 Session 作爲分支引用,讓你可以方便地從任意消息節點切出新分支。

  • 目錄頁:https://www.skillhub.cn/plugins/AnichHaveatry/dsh-client-ui-branch-map
  • GitHub:https://github.com/AnichHaveatry/dsh-client-ui-branch-map
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜